pthread mutex 사용시
글쓴이: girneter / 작성시간: 금, 2003/08/01 - 2:37오후
꼭 pthread mutex 뿐만 아니라
conditional variable 을 쓰는곳에서는 항상 그러던데....
아래 code 를 보면요,
( 어떤 분이 올리셨던걸 좋은 예제라
무단으로 긁어왔습니다. 죄송함다. )
void Queue::put(QItem item) { pthread_mutex_lock(&qmutex); elems_.push_back(item); pthread_cond_signal(&qcond); pthread_mutex_unlock(&qmutex); } QItem Queue::get() { pthread_mutex_lock(&qmutex); while (elems_.empty()) { pthread_cond_wait(&qcond, &qmutex); } QItem ret = *(elems_.begin()); elems_.pop_front(); pthread_mutex_unlock(&qmutex); return ret; }
queue 에서 get 할 때 queue 가 비어있으면
pthread_cond_wait 를 하는데
이 때 왜
if (elems_.empty()) { pthread_cond_wait(&qcond, &qmutex); }
로 하지 않고,
while (elems_.empty()) { pthread_cond_wait(&qcond, &qmutex); }
로 하는건가요?
어차피 pthread_cond_signal 을 보내는 경우는
queue 에 뭔가를 넣은 이후이므로
while 문 돌 필요가 없을 듯 싶은데요.
(물론, 어차피 마찬가지니까 whlie 도 상관없지요)
지금껏 제가 이해하기로는
1. pthread_cond_broadcast
를 하는 경우에는 반드시 필요하므로 그에 대한 처리를 하는 것이고,
2. 어차피 if 나 while 이나 마찬가지니까 1번 경우도 있고 해서
관례적으로 그렇게 한다
고 이해하는데 맞는 건가요?
만약 절대 broadcast 를 안 쓸거라면 if 를 써도 괜찮을까요?
Forums:
[quote]1. pthread_cond_broadcast 를 하는
때에 따라 꼭 필요할 수 있습니다.
pthread_cond_signal()이 호출되는 순간 qcond를 기다리는 여러 thread는 qmutex를 획득하려고 시도합니다(pthread_cond_wait함수 내부에서).
이때 qmutex를 획득하려는 thread가 pthread_cond_wait함수로 qcond을 기다리는 thread 말고도 존재 할 수 있겠죠.
결국 pthread_cond_signal을 호출한 thread가 qmutex를 unlock 하는 순간에 qcond condition을 기다리던 thread와 단지 qmutex를 획득하려던 thread가 경합을 벌이게 됩니다.
이때 단지 qmutex를 획득하려던 thread가 mutex를 획득하여 큐에서 item을 빼고 mutex를 release 한다면 pthread_cond_wait에서 깨어난 thread입장에서 보면 큐에 아무것도 안남아 있겠죠.
대개 worker를 설계 할때 큐에 item이 들어있으면 계속해서 하나씩 처리하다가 하나도 안남았을때에 condition을 기다리도록 설계 합니다.
이런 경우에 위와 같은 상황이 발생할 수 있겠죠.
우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자
물론 worker 별로 큐를 따로 둔다든지 해서 while로 check를
물론 worker 별로 큐를 따로 둔다든지 해서 while로 check를 하지 않도록 설계할 수도 있겠습니다.
우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자
아.. 이해했습니다.
첨엔 무슨 말씀인가 했다가
이제 이해했습니다.
while 문은 반드시 필요하네요. :P
그러니까 A thread 가
Queue::get() 를 call 해서,
pthread_cond_wait(&qcond, &qmutex);
에서 signal 을 받기를 기다리고 있고
B thread 가
Queue::put(QItem item) 을 호출해서
pthread_mutex_lock(&qmutex);
에서 mutex 를 얻었는데
이 때 C thread 가
Queue::get() 를 call 하면
pthread_mutex_lock(&qmutex);
에서 기다리겠죠.
그러다가 B thread 가 signal 을 날리면
A thread 는 mutex 를 얻을 준비를 하고 있는데
C 와 경합을 하다가
mutex 를 C 가 가로채 버리면 A 는 C 가 mutex 를 놓을때까지
기다릴 수 밖에 없고 그 동안에
C 는 queue 가 비어있지 않으므로 아무 문제없이
QItem 을 빼 간 후에 mutex 를 놓으면
A 는 mutex 를 얻어서 진행하려 하지만
이미 queue 는 비어 있겠네요.
그러므로 반드시 while 문으로 check 해야 하고...
도움 주셔서 정말 감사합니다.
개념없는 초딩들은 좋은 말로 할때 DC나 웃대가서 놀아라. 응?
pthread_cond_signal의 행동은 다음과 같이 정의되어 있습니
pthread_cond_signal의 행동은 다음과 같이 정의되어 있습니다.
따라서 stoneshim님께서 설명해 주신 좋은 예 외에도 pthread_cond_signal자체가 여러개의 thread를 깨워버릴 수도 있기 때문에 while이 필요합니다.
[quote]pthread_cond_signal의 행동은 다음과 같이 정
아.. 저는 하나만 깨어나는것으로 알고 있었습니다.. 모든 implementation에서의 행동을 확신할 수는 없는 모양이군요. posix에서 어떤 정의를 내리고 있는지 궁금하네요... 나중에 한번 찾아봐야 겠습니다.
다음은 linux man page의 내용입니다.
sliver님께서 인용하신 부분이 어떤 implementation인지 확인해 주신다면 더 많은 도움이 될것 같습니다.
우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자
앗...인용을 해놓고서는 출처를 안썼네요-_-ㅋㅋ출처는 posix
앗...인용을 해놓고서는 출처를 안썼네요-_-ㅋㅋ
출처는 posix 표준문서 입니다.
정확히는 IEEE Std 1003.1-2001
Standard for Information Technology— Portable Operating System Interface (POSIX) System Interfaces, Issue 6 입니다.
표준이었군요. 감사합니다.
표준이었군요. 감사합니다.
우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자
사실상 대부분의 경우에 하나만 깨어날 겁니다.."Programmi
사실상 대부분의 경우에 하나만 깨어날 겁니다..
"Programming with POSIX Thread"나 "Pthread programming"에서도 하나만 깨어나는 것처럼 설명해 놓고 있죠..
그치만, spec 상에서는 분명히 하나 이상이 깨어날 수 있는 게 맞습니다. 그 이유는 정확히 하나만 깨어나도록 하는 것이 "어떤 구현(특히 멀티프로세서 시스템) 상에서는 매우 어려울 수도 있다" 고 하는군요.. (왜 어려운지는 모르겠습니다. 혹시 아시는 분 있으면 좀 갈켜 주세요) 그런 이유로 pthread spec에서 적어도 하나의 쓰레드가 깨어나도록 규정한 것이겠죠..
"Programming with POSIX Thread"에서는 pthread_cond_wait() 에서 while 문으로 술어 검사를 해 줘야 하는 이유를 다음의 세 가지로 설명하고 있습니다.
1. 가로채인 기상(intercepted wakeup)
stoneshim 님이 설명해 주신 것처럼, 대기하던 쓰레드가 시그널을 받고 진행하려던 차에, 다른 쓰레드가 먼저 뮤텍스를 획득해 버리는 것이죠..
2. 가짜 기상(spurious wakeup)
이 경우는 크게 두 가지가 있습니다.
(1) 이게 바로 silver 님이 얘기해 주신 사항입니다.
특정 하드웨어(특히 멀티프로세서) 상에서는 정확히 한 개의 쓰레드만 깨우는 것이 실질적으로 어렵기 (또는 구현이 매우 복잡하여 많은 비용이 들기) 때문입니다. 따라서 스펙에서 아예 여러 개의 쓰레드를 깨울 수 있도록 규정해 놓은 것이죠..
(2) 또한 이 경우가 발생할 수 있는 예로는, 여러 술어가 하나의 조건 변수를 공유하는 경우 발생합니다. 대부분의 경우, 이렇게 하는 것은 매우 좋지 못한 방법이죠 (가급적 피해야 합니다.)
예를 들면, "큐가 비어 있다." 와 "큐가 가득 찼다" 를 알리기 위해 동일한 조건 변수를 사용하는 것입니다. 이 경우에 "큐가 비어 있다." 는 조건에서 대기하던 쓰레드가 "큐가 가득 찼다"는 사실을 알리는 시그널을 받아 버리면 실질적으로 아무런 동작도 할 수 없습니다. 따라서 while 문의 술어 검사를 통해 다시 대기해야겠죠.
물론 조건 변수의 공유는 시그널을 보내기 위해 pthread_cond_broadcast를 사용한다는 가정 하에서만 성립하며, 그렇지 않을 경우 대부분 데드락을 유발하게 됩니다.
3. 막연한 술어(loose predicate)
이건 다분히 프로그래밍 디자인 관점일 뿐입니다. 그러니까 시그널을 보낼 때, "정말 정확히 시그널을 보내야 하는 상황" 인지를 검사하기가 쉽지 않을 수도 있다는 거죠..
그렇다면, 차라리 "이 시점에서는 시그널을 한번쯤 보내줘야 하지 않을까?" 싶을 때에 시그널을 보내는 방법을 사용할 수도 있습니다. 이 때, 사실 조건 변수에서 기다리는 쓰레드의 입장에서는 시그널을 받았더라도 실제로는 "자신이 원하는 정확한 상황"이 아닐 수도 있습니다. 그렇다면 while 루프를 써서 다시 기다려야겠죠..
즉, 시그널을 보내는 쪽에서 "정확한 조건 판단"이 힘든 경우, 받는 쪽에서 정확한 조건 판단을 하기 위해 while 루프를 사용합니다.
정리하면..
이렇게 세 가지 예가 있는데, 사실 1과 2-(1)의 경우는 스펙에서 규정하는 바대로 정확한 동작을 보장하기 위한 것이고, 2-(2)와 3의 경우는 디자인 관점에서의 이슈라고 볼 수 있습니다.
결론적으로, 이러저러한 상황을 모두 만족시키기 위해 조건 변수의 대기는 while 루프에서 이루어져야 합니다!!!
질문이...
mutex접한지 얼마안되 헤깔리는 부분이 많네요
위에서 볼드체로 된 부분이 무슨의미인지 잘 모르겠습니다.
"큐가 비어있다"는 조건하에 대기하는 것이니까 "큐가 가득 찼다"는 시그널에 동작해야 하는게 아니라 "큐가 비어있지 않다"라는 시그널에 동작해야 한다는 뜻인지...궁금하군요 T.T
설명부탁드려도 될까요?
[quote]예를 들면, "큐가 비어 있다." 와 "큐가 가득 찼다" 를
먼저 "술어를 공유한다"는 개념을 이해하셔야 합니다.
예를 들어, 어떤 큐에 대해 한 쓰레드 A가 "큐가 가득 차다"는 조건이 참이 되기를 기다린다고 해 봅시다. 그 때, "큐가 가득 차다" 와 같은 조건을 나타내는 문장을 "술어(predicate)" 라고 합니다.
이 큐에 대해서 다른 쓰레드 B는 "큐가 비어 있다" 는 조건을 기다린다고 해 봅시다. 즉, 쓰레드 B는 다른 술어 조건이 참이 되길 기다리는 것이죠.
이 때, 술어를 기다리기 위해 조건 변수를 사용합니다. 즉, 위의 경우 다음과 같이 조건 변수를 선언할 수 있겠죠.
보통 위와 같이 각각의 술어에 대해 다른 조건 변수를 사용하는 것이 일반적인 방법입니다.
그러나, 어떤 사람이 디자인 상의 어떤 이유로 큐에 변화가 일어나는(큐가 비었다거나 큐가 가득 찼다거나, 큐에 아이템을 삽입하거나 제거했다거나) 상황을 모두 하나의 조건 변수에 대응시켰다고 가정해 봅시다. 이제 조건 변수는 다음과 같이 하나만 필요하겠죠.
이 상황에서는
쓰레드 A도 queue_modify에서 대기하고(pthread_cond_wait)고 있으며, 쓰레드 B도 queue_modify에서 대기하고 있을 것입니다.
이런 경우를 여러 개의 술어가 하나의 조건 변수를 공유한다고 표현합니다. 즉, "큐가 비어 있다", "큐가 가득 찼다" 와 같은 분명히 다른 상황을 나타내는 술어지만, queue_modify 조건 변수 하나가 모두 처리하고 있는 것이죠.
이 때, 문제는 이렇습니다.
이렇게 조건 변수를 공유하는 상황에서는, 해당 조건 변수(queue_modify)에 대해 "절대" pthread_cond_signal 을 사용해서는 안 됩니다.
그 이유는 "큐가 비어 있다"는 사실을 알리기 위해 시그널을 보냈을 때, 그 시그널을 "큐가 가득 차 있다"를 기다리고 있는 쓰레드가 받아 버릴 수 있는 것이죠.. 그렇게 되면 데드락이 유발되기 쉽습니다.
따라서, 조건 변수를 공유하는 상황에서는 반드시 pthread_cond_broadcast 를 사용해야 합니다. 이렇게 하면 대기 중인 모든 쓰레드가 시그널을 받겠지요..
단, 이 때 "큐가 비어 있다"는 사실을 알리기 위한 시그널은 정말 "큐가 비어 있다"는 조건을 기다리고 있는 쓰레드에게만 유효해야 합니다. "큐가 가득 차 있다"는 조건을 기다리는 쓰레드는 적절히 술어 조건을 검사한 후, 다시 잠들어야 겠지요..
위에서 "자신에게 유효한 조건이 아닌 시그널을 받은" 쓰레드가 다시 술어를 검사하고, 조건 변수에서 대기하기 위해서 바로 while 루프가 필요한 것입니다.
설명이 너무 길어졌네요.. 두서가 없었는데 도움이 되시길 바랍니다.
아...
도움 됐어요!
답변 감사드립니다 ^__^
댓글 달기