이런 상황을 생각해보자. 쓰레드가 있고, 그것은 평상시에 자는 상태(CPU점유율 0%)이다. 그런데 문득 할 일이 생겨서 침 흘리며 자고 있는 쓰레드를 두들겨 깨우고 싶다. 어떻게 하면 좋을까?
그래서 나온 녀석이 Condition이다. 술 먹기 전에 한 번 먹고, 먹은 다음 날 한 번 먹는다는 그것!...은 아니다.
대충 보면, 쓰레드를 n개(여기서는 3개) 만들고, 컨디션 하나, 뮤텍스 하나 만든다. (나머진 생략) thr은 signal이 올 때까지 pthread_cond_wait에서 퍼질러 잔다. 이때 main함수에서 pthread_cond_signal을 보내면, 자고 있던 쓰레드가 벌떡! 깨어난다. 깨는 순서는 순전히 운에 달려 있다. 또한 pthread_cond_broadcast로 모든 thr를 깨울 수도 있다.
중요한 것은 condition이 깨는 순간, 파라메터로 넣어준 mutex를 lock하는데, 뭔가 다 끝났으면 잊지 말고 unlock해주자. 이러한 속성을 이용해서 signal/broadcast 앞뒤로 해당 mutex를 lock하고, 보호하고자 하는 것이 있으면 그 사이와 cond_wait~mutex_unlock 사이에 아름답게 써주자.
다음은 위를 컴파일하고 실행한 결과이다.
volatile bool gWakeUp(false);
void*
_thr(void*)
{
while (!gWakeUp) usleep(1000*1000);
// something...
}
int
main(int,char**)
{
// ... init thread.
gWakeUp = true;
return 0;
}
이러면 쓰것나? 뭔가 찜찜허다. gWakeUp이 true로 바뀌더라도 _thr는 최악의 경우 1초 뒤에 반응을 보일 것이다. 아힝~* 싫어~*그래서 나온 녀석이 Condition이다. 술 먹기 전에 한 번 먹고, 먹은 다음 날 한 번 먹는다는 그것!...은 아니다.
#include <cstdarg>
#include <pthread.h>
#include <iostream>
using namespace std;
pthread_cond_t cond;
pthread_mutex_t mtx;
pthread_attr_t attr;
// Just for console.
pthread_mutex_t console_lock;
void
display(const char* fmt, ...)
{
pthread_mutex_lock(&console_lock);
va_list lst;
va_start(lst, fmt);
vfprintf(stderr, fmt, lst);
va_end(lst);
pthread_mutex_unlock(&console_lock);
}
void*
thr(void* param)
{
const size_t no((size_t)param);
display("no. %u: I'm born!\n", no);
do
{
pthread_cond_wait(&cond, &mtx);
// mtx will be locked.
//- If you want protect something,
// write statments here.
pthread_mutex_unlock(&mtx);
display("no. %u: Yes, Sir!\n", no);
// hard work something...
usleep((rand()%1000)*1000);
display("no. %u: I'm sleeping...\n", no);
} while (true);
pthread_exit(NULL);
}
int
main(int,char**)
{
srand((unsigned int)time(NULL));
pthread_t hdl;
// Initialize mutex & condition.
pthread_mutex_init(&mtx, NULL);
pthread_cond_init(&cond, NULL);
// Console mutex.
pthread_mutex_init(&console_lock, NULL);
// Thread attributes.
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_PROCESS);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// Create n threads!
for ( size_t i(0); i<3; i++ )
{
pthread_create(&hdl, &attr, thr, (void*)i);
}
// Signal condition.
for ( size_t i(0); i<10; i++ )
{
display("Commander: Wake up, Thread!\n");
pthread_cond_signal(&cond);
usleep((rand()%1000)*1000);
}
// Broadcast signal condition.
display("Commander: Wake up all!\n");
pthread_cond_broadcast(&cond);
usleep(1000000*10);
return 0;
}
자자, 겁먹지 말자.대충 보면, 쓰레드를 n개(여기서는 3개) 만들고, 컨디션 하나, 뮤텍스 하나 만든다. (나머진 생략) thr은 signal이 올 때까지 pthread_cond_wait에서 퍼질러 잔다. 이때 main함수에서 pthread_cond_signal을 보내면, 자고 있던 쓰레드가 벌떡! 깨어난다. 깨는 순서는 순전히 운에 달려 있다. 또한 pthread_cond_broadcast로 모든 thr를 깨울 수도 있다.
중요한 것은 condition이 깨는 순간, 파라메터로 넣어준 mutex를 lock하는데, 뭔가 다 끝났으면 잊지 말고 unlock해주자. 이러한 속성을 이용해서 signal/broadcast 앞뒤로 해당 mutex를 lock하고, 보호하고자 하는 것이 있으면 그 사이와 cond_wait~mutex_unlock 사이에 아름답게 써주자.
다음은 위를 컴파일하고 실행한 결과이다.
$ make
g++ -lpthread cond.cpp -o cond
$ ./cond
Commander: Wake up, Thread! - 1
no. 0: I'm born!
no. 1: I'm born!
no. 2: I'm born!
Commander: Wake up, Thread!
no. 0: Yes, Sir!
Commander: Wake up, Thread!
no. 1: Yes, Sir!
no. 0: I'm sleeping...
no. 1: I'm sleeping...
Commander: Wake up, Thread!
no. 2: Yes, Sir!
Commander: Wake up, Thread!
no. 0: Yes, Sir!
no. 2: I'm sleeping...
Commander: Wake up, Thread!
no. 1: Yes, Sir!
Commander: Wake up, Thread!
no. 2: Yes, Sir!
Commander: Wake up, Thread!
no. 1: I'm sleeping...
no. 0: I'm sleeping...
Commander: Wake up, Thread!
no. 1: Yes, Sir!
no. 2: I'm sleeping...
Commander: Wake up, Thread!
no. 0: Yes, Sir! - 2
no. 1: I'm sleeping...
Commander: Wake up all! - 3
no. 2: Yes, Sir!
no. 1: Yes, Sir!
no. 0: I'm sleeping... - 4
no. 1: I'm sleeping...
no. 2: I'm sleeping...
실행 결과에서 알 수 있듯이 쓰레드 컨디션에 도달하기도 전에 Signal을 보낸 것(1)은 받은 녀석이 없다. 또한 모두 깨웠는데(3) 모든 쓰레드가 시그널을 받지 못한 까닭은 해당 쓰레드가 cond_wait 상태가 아니었기 때문이다. (2, 4)
댓글
댓글 쓰기