복습
프로세스는 운영체제로부터 자원을 할당받는 작업의 단위이고 스레드는 프로세스가 할당받은 자원을 이용하는 실행의 단위이다.
멀티 스레드는 멀티 프로세스보다 적은 메모리 공간을 차지하고 Context Switching이 빠른 장점이 있지만, 동기화 문제와 하나의 스레드 장애로 전체 스레드가 종료 될 위험을 갖고 있다.
멀티 프로세스는 하나의 프로세스가 죽더라도 다른 프로세스에 영향을 주지 않아 안정성이 높지만, 멀티 스레드보다 많은 메모리공간과 CPU 시간을 차지하는 단점이 있다.
두 방법은 동시에 여러 작업을 수행하는 점에서 동일하지만, 결국 각각의 장단이 있으므로 적용하는 시스템에 따라 적합한 동작 방식을 선택하고 적용해야 한다.
운영체제가 시스템 자원을 효율적으로 관리하기 위해 스레드를 사용한다.
멀티 프로세스로 실행되는 작업을 멀티 스레드로 실행할 경우, 프로세스를 생성하여 자원을 할당하는 시스템 콜이 줄어들어 자원을 효율적으로 관리할 수 있다.
또한, 프로세스 간의 통신보다 스레드 간의 통신 비용이 적으므로 작업들 간 통신의 부담이 줄어든다. (처리비용 감소. 프로세스는 독립구조이기 때문)
스레드를 활용하면 자원의 효율성이 증가하기도 하지만, 스레드 간의 자원 공유는 전역 변수를 이용하므로 동기화 문제가 발생 할 수 있으므로 프로그래머의 주의가 필요하다.
특히 동기화가 중요한데 동기화란
프로세스 또는 스레드들이 수행되는 시점을 조절하여 서로가 알고 있는 정보가 일치하는 것을 의미한다
멀티 스레드의 환경에서 여러 스레드가 동시에 같은 작업을 진행한다고 가정하자.
그럴 경우 프로그래머가 예측한 결과와 다른 결과가 나오는 경우들이 있는데,
간단히 살펴보면
public class Main {
int count = 0;
public void increaseCount() {
count++;
}
}
위와같은 코드가 있다고 가정하고 increaseCount라는 메소드를 여러 스레드가 동시에 실행했다고 가정하면 각각의 스레드가 5번씩 총 10번 실행했다 하더라도 count는 10이 아닐 수 있다. 각 스레드가 동시에 실행하면서 dirty read와 같은 문제들로 결과값이 우리가 생각했던 것과 다를 수 있는 것이다.
이러한 문제를 해결하는 것을 동기화한다고 한다.
여러 스레드가 동시에 접근해도 서로의 데이터를 일치시켜 예상하던 결과로 나오게 하는 것이다.
이렇게 동기화가 필요한 영역을 임계 영역(critical section)이라 한다.
임계 구역(critical section) 또는 공유변수 영역은 병렬컴퓨팅에서 둘 이상의 스레드가 동시에 접근해서는 안되는 공유 자원(자료 구조 또는 장치)을 접근하는 코드의 일부를 말한다.
임계 구역 문제를 해결하기 위해서는 다음 3가지 조건을 충족해야 한다.
상호 배제(Mutual exclusion) : 하나의 프로세스가 임계 구역에 들어가 있다면 다른 프로세스는 들어갈 수 없어야 한다.
진행(Progress) : 임계 구역에 들어간 프로세스가 없는 상태에서, 들어가려고 하는 프로세스가 여러 개 있다면 어느 것이 들어갈지를 적절히 결정해주어야 한다.
한정 대기(Bounded waiting) : 다른 프로세스의 기아(Starvation)를 방지하기 위해, 한 번 임계 구역에 들어간 프로세스는 다음 번 임계 구역에 들어갈 때 제한을 두어야 한다.
방법으로는 뮤텍스, 세마포어, 모니터 등이 있다.
스핀 락(Spin lock)은 임계 구역에 진입이 불가능할 때 진입이 가능할 때까지 루프를 돌면서 재시도하는 방식으로 구현된 락을 가리킨다.
임계 구역 진입 전까진 루프를 계속 돌고 있기 때문에 busy waiting이 발생하게 된다.
짧은 시간 안에 진입할 수 있는 경우 문맥 교환 비용이 들지 않으므로 효율을 높일 수 있지만 그 반대의 경우에는 다른 스레드에 cpu를 양보하지 않기 때문에 오히려 cpu 효율을 떨어뜨리게 된다.
ex)
wait(S) {
while (S <= 0); // 자원이 없다면 while 루프를 돌며 대기를 함.
S--; // 자원을 획득함.
}
signal(S) {
S++; // 자원을 해제함.
}
뮤텍스는 자원에 대한 접근을 동기화하기 위해 사용되는 상호 배제 기술이다.
뮤텍스는 Locking 메커니즘으로 락을 걸은 스레드만이 임계 영역을 나갈 때 락을 해제할 수 있다.
잠금 메커니즘이라는 점은 스핀 락과 동일하나 권한을 획득할 때까지 busy waiting 상태에 머무르지 않고
sleep 상태로 들어가고 wakeup 되면 다시 권한 획득을 시도하는 sleep lock을 사용한다.
쉽게 정리하자면 임계 영역에 처음 도달한 스레드만 작업을 시작하고 임계 영역에서 다른 스레드가 작업중이라면 스레드를 재우고, 임계 영역에서 작업을 다 마친 스레드가 자고있는 스레드를 깨우는 구조라는 것이다.
ex)
do {
wait (mutex);
// Critical section
signal (mutex);
// Remainder section
} while (TRUE);
세마포어는 음수가 아닌 정수 값을 가지고 스레드 간에 공유되는 변수이다.
이 변수는 임계 구역 문제를 해결하고 동기화를 구현하는 데 사용된다.
세마포어는 signaling 메커니즘으로 락을 걸지 않은 스레드도 signal을 사용해 락을 해제할 수 있다.
이진 세마포어 (Binary semaphore)
0또는 1 값만 가질 수 있는 세마포어입니다.
임계 구역 문제를 해결하는데 사용하며 자원이 하나이기 때문에 뮤텍스로도 사용할 수 있습니다.
개수 세마포어 (Counting semaphore)
도메인이 0이상인 임의의 정수값인 세마포어입니다.
여러개의 자원을 가질 수 있으며 제한된 자원을 가지고 액세스 작업을 할때 사용합니다.
뮤텍스는 Locking 메커니즘으로 락을 걸은 스레드만이 임계 구역을 나갈 때 락을 해제할 수 있지만 세마포어는 Signaling 메커니즘으로 락을 걸지 않은 스레드도 signal을 사용해 락을 해제할 수 있다.
이러한 차이 때문에 이진 세마포어는 뮤텍스로 사용할 수 있지만 뮤텍스는 세마포어로 사용할 수 없다.
ex)
typedef struct
{
int value; /* semaphore */
struct process *list; /* process wait queue */
} semaphore;
wait(semaphore *S) {
S->value--;
if (S->value < 0 ) { // 자원이 없다면
add this process to S->list; // 프로세스를 큐에 넣고
block(); // block 시킴
}
}
signal(semaphore *S) {
S->value++;
if (S->value <= 0) { // 자원이 0이하라면 block중인 프로세스가 있다는 의미임.
remove a process P from S->list; // 대기하고 있는 프로세스를 가져옴.
wakeup(P); // 가져온 프로세스를 깨움.
}
}
https://yoongrammer.tistory.com/63
https://ko.wikipedia.org/wiki/%EC%9E%84%EA%B3%84_%EA%B5%AC%EC%97%AD_%EB%AC%B8%EC%A0%9C