조건 변수를 사용하는 이유

    이전 글(https://shyeon.tistory.com/48)에서 mutex를 이용하여 스레드간 동기화를 하는 방법을 알아 보았다. 하지만 mutex만을 이용하여 동기화를 하는 것은 프로세스가 mutex변수를 지속적으로 검사해야 한다는 단점이 있다. 따라서 이 글에서 다루는 조건 변수(condition)을 함께 사용한다. 

 

함수

함수는 mutex함수와 굉장히 유사하다.

1. pthread_cond_init

int pthread_cond_init(pthread_cond_t *restrict condition, pthread_condattr_t *restrict attr);

mutex변수를 초기화하는 것처럼 cond변수도 초기화를 해야한다. 첫 번째 인자는 초기화하려는 cond 변수의 주소값을 넘기고 두 번째 인자는 cond 변수의 속성을 설정하는 변수이고 리눅스에선 무시하므로 NULL값을 주로 넣는다.

mutex 변수처럼 pthread_cond_init함수 말고 직점 변수에 PTHREAD_COND_INITIALIZER를 대입하여 초기화하는 방법도 있다.

 

2. pthread_cond_destroy

int pthread_cond_destroy(pthread_cond_t *condition);

mutex 변수와 마찬가지로 사용이 끝난 cond 변수는 pthread_cond_destroy함수를 호출하여 해제해줘야 한다.

 

3. pthread_cond_signal

int pthread_cond_signal(pthread_cond_t *cond);

cond 변수에 신호를 보내어 cond 변수를 기다리고 있는 스레드를 다시 시작시키는 함수이다. 만약 cond 변수를 기다리는 스레드가 없다면 아무 일도 일어나지 않느다. pthread_cond_signal함수는 cond 변수를 기다리는 여러 스레드 중에 단 하나만 다시 시작시킨다. 하지만 어떤 스레드를 시작시킬지는 지정할 수 없다.

 

4. pthread_cond_broadcast

int pthread_cond_broadcast(pthread_cond_t *cond);

하나의 스레드만 다시 시작시키는 pthread_cond_signal 함수와 다르게 cond 변수를 기다리고 있는 모든 스레드를 다시 시작시킨다.

 

5. pthread_cond_wait

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

cond 변수가 신호를 받을 때까지 기다리는 함수이다. 이 함수가 실행되면 mutex를 lock을 해제하고 기다린다. 따라서 다른 스레드에서는 mutex변수의 lock을 얻을 수 있다. 그 다음 이 함수를 벗어날 경우 mutex lock을 얻게 된다. 기다리는 시간을 지정할 수 있는 pthread_cond_timedwait도 있다.

 

예제

이 예제는 1부터 10까지의 숫자를 두 개의 스레드에서 출력한다. 1~3은 1번 스레드가, 4~7은 2번 스레드가, 8~10은 다시 1번 스레드가 실행한다. 1번 스레드가 실행할 부분은 2번 스레드에서 신호를 주어 cond변수를 이용하여 다시시작한다.

 

소스 코드

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //변수 초기화
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int value = 0; //공유 변수

void *func1(void *arg) { //스레드1이 실행할 함수
	while(1) {
		pthread_mutex_lock(&mutex); //lock을 걸고
		pthread_cond_wait(&cond, &mutex); //기다림
		value++; //1 증가시키고
		printf("thread1 : %d\n", value); //출력

		pthread_mutex_unlock(&mutex); //lock 해제

		if(value >= 10) //10을 넘어갈 경우 종료
			return NULL;
	}
}

void *func2(void *arg) { //스레드2가 실행할 함수
	while(1) {
		pthread_mutex_lock(&mutex); //lock을 걸고

		if(value < 3 || value > 6) { //1번 스레드가 실행할 부분
			pthread_cond_signal(&cond); //cond변수를 기다리는 스레드 1번을 다시 시작시킴
		}
		else { //그 외의 부분은
			value++; //1 증가시키고
			printf("thread2 : %d\n", value); //출력
		}

		pthread_mutex_unlock(&mutex); //lock 해제

		if(value >= 10)
			return NULL;
	}
}

int main() {
	pthread_t tid1, tid2;

	pthread_create(&tid1, NULL, &func1, NULL); //스레드 1 생성
	pthread_create(&tid2, NULL, &func2, NULL); //스레드 2 생성

	pthread_join(tid1, NULL); //스레드1 리소스 반환
	pthread_join(tid2, NULL); //스레드2 리소스 반환

	pthread_mutex_destroy(&mutex); //mutex 변수 해제
	pthread_cond_destroy(&cond); //cond 변수 해제

	return 0;
}

 

실행 결과

1~3, 8~9는 1번 스레드가 출력하고 4~7은 2번 스레드가 출력하는 것을 볼 수 있다.

+ Recent posts