1. 首先pthread_cond_wait 的定義是這樣的
The pthread_cond_wait() and pthread_cond_timedwait() functions are used to block on a condition variable. They are called with mutex locked by the calling thread or undefined behaviour will result.
These functions atomically release mutex and cause the calling thread to block on the condition variable cond ; atomically here means "atomically with respect to access by another thread to the mutex and then the condition variable". That is, if another thread is able to acquire the mutex after the about-to-block thread has released it, then a subsequent call to pthread_cond_signal() or pthread_cond_broadcast() in that thread behaves as if it were issued after the about-to-block thread has blocked.
2. 由上解釋可以看出,pthread_cond_wait() 必須與pthread_mutex 配套使用。
pthread_cond_wait()函數一進(jìn)入wait狀態(tài)就會(huì )自動(dòng)release mutex.
In Thread1:
pthread_mutex_lock(&m_mutex);
pthread_cond_wait(&m_cond,&m_mutex);
pthread_mutex_unlock(&m_mutex);
In Thread2:
pthread_mutex_lock(&m_mutex);
pthread_cond_signal(&m_cond);
pthread_mutex_unlock(&m_mutex);
為什么要與pthread_mutex 一起使用呢? 這是為了應對線(xiàn)程1在調用pthread_cond_wait()但線(xiàn)程1還沒(méi)有進(jìn)入wait cond的狀態(tài)的時(shí)候,此時(shí)線(xiàn)程2調用了 cond_singal 的情況。 如果不用mutex鎖的話(huà),這個(gè)cond_singal就丟失了。加了鎖的情況是,線(xiàn)程2必須等到 mutex 被釋放(也就是 pthread_cod_wait() 進(jìn)入wait_cond狀態(tài) 并自動(dòng)釋放mutex) 的時(shí)候才能調用cond_singal.
3. pthread_cond_wait() 一旦wait成功獲得cond 條件的時(shí)候會(huì )自動(dòng) lock mutex.
這就會(huì )出現另一個(gè)問(wèn)題。這是因為
The pthread_cond_wait() and pthread_cond_timedwait() is a cancellation point.
In Thread3:
pthread_cancel(&m_thread);
pthread_join();
因為pthread_cond_wait() and pthread_cond_timedwait() 是線(xiàn)程退出點(diǎn)函數,因此在Thread3中
可以調用pthread_cancel()來(lái)退出線(xiàn)程1。那樣顯然線(xiàn)程1會(huì )在 pthread_cond_wait(&m_cond,&m_mutex); 和 pthread_mutex_unlock(&m_mutex); 之間退出, pthread_cond_wait() 函數返回后自動(dòng)lock住了mutex, 這個(gè)時(shí)候線(xiàn)程1退出(并沒(méi)有運行到pthread_mutex_unlock()),如果Thread2這個(gè)時(shí)候就再也得不到lock狀態(tài)了。
通常解決這個(gè)問(wèn)題的辦法如下
void
cleanup(void
*arg)
{
pthread_mutex_unlock(&mutex);
}
void
* thread1(void
* arg)
{
pthread_cleanup_push(cleanup, NULL); // thread cleanup handler
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
pthread_cleanup_pop(0
);
}
LINUX環(huán)境下多線(xiàn)程編程肯定會(huì )遇到需要條件變量的情況,此時(shí)必然要使用pthread_cond_wait()函數。但這個(gè)函數的執行過(guò)程比較難于理解。
pthread_cond_wait()的工作流程如下(以MAN中的EXAMPLE為例):
Consider two shared variables x and y, protected by the mutex mut, and a condition vari-
able cond that is to be signaled whenever x becomes greater than y.
int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
Waiting until x is greater than y is performed as follows:
pthread_mutex_lock(&mut);
while (x <= y) {
pthread_cond_wait(&cond, &mut);
}
/* operate on x and y */
pthread_mutex_unlock(&mut);
Modifications on x and y that may cause x to become greater than y should signal the con-
dition if needed:
pthread_mutex_lock(&mut);
/* modify x and y */
if (x > y) pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);
這個(gè)例子的意思是,兩個(gè)線(xiàn)程要修改X和 Y的值,第一個(gè)線(xiàn)程當X<=Y時(shí)就掛起,直到X>Y時(shí)才繼續執行(由第二個(gè)線(xiàn)程可能會(huì )修改X,Y的值,當X>Y時(shí)喚醒第一個(gè)線(xiàn)程),即 首先初始化一個(gè)普通互斥量mut和一個(gè)條件變量cond。之后分別在兩個(gè)線(xiàn)程中分別執行如下函數體:
pthread_mutex_lock(&mut);和:
pthread_mutex_lock(&mut);
/* modify x and y */
if (x > y) pthread_cond_signal(&cond);
pthread_mutex_unlock(&mut);
其實(shí)函數的執行過(guò)程非常簡(jiǎn)單,在第一個(gè)線(xiàn)程執行到pthread_cond_wait(&cond,&mut)時(shí),此時(shí)如果X<=Y,則此函數就將mut互斥量解鎖
,再將cond條件變量加鎖
,此時(shí)第一個(gè)線(xiàn)程掛起
(不占用任何CPU周期)。
而在第二個(gè)線(xiàn)程中,本來(lái)因為mut被第一個(gè)線(xiàn)程鎖住而阻塞,此時(shí)因為mut已經(jīng)釋放,所以可以獲得鎖mut,并且進(jìn)行修改X和Y的值,在修改之后,一個(gè)IF語(yǔ)句判定是不是X>Y,如果是,則此時(shí)pthread_cond_signal()函數會(huì )喚醒第一個(gè)線(xiàn)程
,并在下一句中釋放互斥量mut。然后第一個(gè)線(xiàn)程開(kāi)始從pthread_cond_wait()執行,首先要再次鎖mut
, 如果鎖成功,再進(jìn)行條件的判斷
(至于為什么用WHILE,即在被喚醒之后還要再判斷,后面有原因分析),如果滿(mǎn)足條件,則被喚醒
進(jìn)行處理,最后釋放互斥量mut
。
至于為什么在被喚醒之后還要再次進(jìn)行條件判斷(即為什么要使用while循環(huán)來(lái)判斷條件),是因為可能有“驚群效應”。有人覺(jué)得此處既然是被喚醒的,肯定 是滿(mǎn)足條件了,其實(shí)不然。如果是多個(gè)線(xiàn)程都在等待這個(gè)條件,而同時(shí)只能有一個(gè)線(xiàn)程進(jìn)行處理,此時(shí)就必須要再次條件判斷,以使只有一個(gè)線(xiàn)程進(jìn)入臨界區處理。 對此,轉來(lái)一段:
引用下POSIX的RATIONALE:
Condition Wait Semantics
It is important to note that when pthread_cond_wait() and
pthread_cond_timedwait() return without error, the associated predicate
may still be false. Similarly, when pthread_cond_timedwait() returns
with the timeout error, the associated predicate may be true due to an
unavoidable race between the expiration of the timeout and the
predicate state change.
The application needs to recheck the predicate on any return because it
cannot be sure there is another thread waiting on the thread to handle
the signal, and if there is not then the signal is lost. The burden is
on the application to check the predicate.
Some implementations, particularly on a multi-processor, may sometimes
cause multiple threads to wake up when the condition variable is
signaled simultaneously on different processors.
In general, whenever a condition wait returns, the thread has to
re-evaluate the predicate associated with the condition wait to
determine whether it can safely proceed, should wait again, or should
declare a timeout. A return from the wait does not imply that the
associated predicate is either true or false.
It is thus recommended that a condition wait be enclosed in the equivalent of a "while loop" that checks the predicate.
從上文可以看出:
1,pthread_cond_signal在多處理器上可能同時(shí)喚醒多個(gè)線(xiàn)程,當你只能讓一個(gè)線(xiàn)程處理某個(gè)任務(wù)時(shí),其它被喚醒的線(xiàn)程就需要繼續
wait,while循環(huán)的意義就體現在這里了,而且規范要求pthread_cond_signal至少喚醒一個(gè)pthread_cond_wait上
的線(xiàn)程,其實(shí)有些實(shí)現為了簡(jiǎn)單在單處理器上也會(huì )喚醒多個(gè)線(xiàn)程.
2,某些應用,如線(xiàn)程池,pthread_cond_broadcast喚醒全部線(xiàn)程,但我們通常只需要一部分線(xiàn)程去做執行任務(wù),所以其它的線(xiàn)程需要繼續wait.所以強烈推薦此處使用while循環(huán).
其實(shí)說(shuō)白了很簡(jiǎn)單,就是pthread_cond_signal()也可能喚醒多個(gè)線(xiàn)程,而如果你同時(shí)只允許一個(gè)線(xiàn)程訪(fǎng)問(wèn)的話(huà),就必須要使用while來(lái)進(jìn)行條件判斷,以保證臨界區內只有一個(gè)線(xiàn)程在處理。
聯(lián)系客服