현생/TCP 소켓 프로그래밍

18-5 쓰레드 동기화 - 세마포어(Semaphore) [C][LINUX]

푸더기 2022. 2. 17. 00:04
반응형

 

세마포어는 18-4에서 다룬 뮤텍스와 유사합니다. 여기서는 0과 1만을 사용하는 바이너리 세마포어라는 것을 대상으로 쓰레드의 실행순서 컨트롤 중심의 동기화를 다룹니다.

#include<semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_destroy(sem_t *sem);
//success:0  fail:0 이외의 값

위 두 함수는 세마포어의 생성 및 소멸에 관한 함수입니다.

sem에는 세마포어의 참조 값 저장을 위한 변수의 주소 값 전달(소멸 시에는 소멸시키려는 세마포어의 참조 값을 저장하고 있는 변수의 주소 값 전달),

pshared에는 0 전달 시 하나의 프로세스 내에서 접근 가능, 0 이외의 값 전달 시 둘 이상의 프로세스에 의해 접근 가능한 세마포어를 생성하는데 여기서는 쓰레드의 동기화를 목적으로 하므로 0을 전달하고,

value에는 생성되는 세마포어의 초기 값을 지정합니다.

 

#include<semaphore.h>

int sem_post(sem_t *sem);
int sem_wait(sem_t *sem);
//success: 0  fail: 0 이외의 값

sem_post에 전달된 세마포어 값은 1 증가하고, sem_wait에 전달된 세마포어 값은 1 감소합니다.세마포어 값은 0보다 작아질 수 없기에 세마포어 값이 0 일때 sem_wait이 호출되면 블로킹 상태에 놓입니다.

즉, sem_post가 unlock, sem_wait가 lock 역할을 하게 됩니다.

 

sem_wait(&sem);
...
sem_post(&sem);

 위와 같이 임계영역을 감싸서 사용할 수 있습니다. 

 

다음은 접근 순서의 동기화와 관련된 예제 시나리오입니다.

쓰레드 A가 전역변수 num에 값을 저장하면 쓰레드 B가 이 값을 가져다가 누적시킨다.

위 시나리오대로 프로그램을 구현하려면 변수 num은 쓰레드 A - 쓰레드 B 순서로 이뤄져야 합니다. 이를 구현하기 위해선 세마포어가 2개 필요합니다.

//전역 변수
static sem_t sem_one, sem_two;
static int num;


//main 함수
...
sem_init(&sem_one,0,0); // 세마포어 초기값 0
sem_init(&sem_two,0,1); // 세마포어 초기값 1
...


// 쓰레드 A
...
for(i=0; i<5; i++){
    sem_wait(&sem_two);
    num=value;
    sem_post(&sem_one);
}
...


// 쓰레드 B
...
for(i=0; i<5; i++){
	sem_wait(&sem_one);
    sum+=num;
    sem_post(&sem_two);
}
...

sem_one의 초기값은 0, sem_two의 초기값은 1입니다. 따라서 처음에 쓰레드 A의 sem_wait(&sem_two)는 반환되고 sem_two는 0이 됩니다. num에 값이 입력되면 sem_one이 1이 됨으로써 쓰레드B의 sem_wait(&sem_one)은 반환되며 sem_one은 다시 0이 됩니다. 쓰레드A가 다시 만난 sem_wait(&sem_two)는 블로킹 됩니다. 쓰레드B에서 sum+=num 연산이 끝나면 sem_two는 1증가하여 블로킹된 쓰레드A의 sem_wait함수가 반환됩니다. 동시에 쓰레드B는 sem_wait함수로 블로킹됩니다. 이 과정이 반복됩니다.

 

반응형