轉:http://blog.csdn.net/aniao/article/details/5802015
APUE上,關於條件鎖。其中有這么幾條總結:
1.使用條件鎖前必須先鎖住對應的互斥鎖。
2.條件鎖進入阻塞(pthread_cond_wait)時自動解開對應互斥鎖,而一旦跳出阻塞立即再次取得互斥鎖,而這兩個操作都是原子操作。
好,現在考慮到這一點,假如有如下函數:
void* run(void *s) { pthread_mutex_lock(&mutex); while(i == 1) { printf("線程%u進入等待狀態\n", (unsigned int)pthread_self()); pthread_cond_wait(&cond_l, &mutex); } printf("已經解開%u\n", (unsigned int)pthread_self()); pthread_mutex_unlock(&mutex); i = 1; return ((void *) 1); }
根據前面兩條規則,我們可以知道,如果多個線程同時調用這個函數,當一個線程取得同步鎖之后,其他線程就會阻塞在pthread_mutex_lock函數,而當那個取得鎖的線程執行到pthread_cond_wait並阻塞之后,在從這個函數返回(條件滿足)之前,會釋放掉鎖,所以其他線程也能一個一個都執行到pthread_cond_wait這里阻塞。這時就有多個線程阻塞在這里了。
假設這時候在另外某個線程條件被滿足,並發出了pthread_cond_signal,那么這么多阻塞的線程會不會全部一下就都被解開了呢?
答案是否。
因為根據第二條規則,從阻塞的函數返回並嘗試再次鎖住互斥鎖,這是一個原子操作。也就是說,第一個成功解套的線程會再次鎖上互斥鎖,而其他線程這時候要想跳出阻塞狀態就不可能了,因為他們無法取得互斥鎖,只能繼續等待(根據我的測試是等待下一次pthread_cond_singal。
(以上是錯誤的,后來發現,原來pthread_cond_signal本來就只會喚醒一個條件鎖,而實驗證明,喚醒的順序跟阻塞在條件鎖的順序相同)
#include <stdio.h> #include <error.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond_l = PTHREAD_COND_INITIALIZER; int i = 1; void* run(void *); void main(int argc, char **argv) { pthread_t pid1; pthread_t pid2; pthread_t pid3; pthread_t pid4; pthread_create(&pid1, NULL, run, NULL ); printf("new thread:%u\n", (unsigned int)pid1); sleep(1); pthread_create(&pid2, NULL, run, NULL ); printf("new thread:%u\n", (unsigned int)pid2); sleep(1); pthread_create(&pid3, NULL, run, NULL ); printf("new thread:%u\n", (unsigned int)pid3); sleep(1); pthread_create(&pid4, NULL, run, NULL ); printf("new thread:%u\n", (unsigned int)pid4); sleep(1); //修改 //pthread_mutex_lock(&mutex); i = 2; pthread_cond_signal(&cond_l); printf("release signal\n"); sleep(1); i = 2; pthread_cond_signal(&cond_l); printf("release signal\n"); sleep(1); pthread_join(pid1, NULL ); pthread_join(pid2, NULL ); pthread_join(pid3, NULL ); pthread_join(pid4, NULL ); } void* run(void *s) { pthread_mutex_lock(&mutex); while(i == 1) { printf("線程%u進入等待狀態\n", (unsigned int)pthread_self()); pthread_cond_wait(&cond_l, &mutex); } printf("已經解開%u\n", (unsigned int)pthread_self()); pthread_mutex_unlock(&mutex); i = 1; return ((void *) 1); }
最后的輸出是:
new thread:3085007776
線程3085007776進入等待狀態
new thread:3076615072
線程3076615072進入等待狀態
new thread:3068222368
線程3068222368進入等待狀態
new thread:3059829664
線程3059829664進入等待狀態
release signal
已經解開3085007776
release signal
已經解開3076615072
一切正常,每次pthread_cond_signal就能放掉一個線程。那么為了驗證前面我的分析是正確的,加入在執行pthread_cond_signal的時候,阻塞在對應條件鎖的pthread_cond_wait處的線程的互斥鎖全都是被鎖住的,還會有線程能成功解套么?看以下代碼:
#include <stdio.h> #include <error.h> #include <pthread.h> #include <unistd.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond_l = PTHREAD_COND_INITIALIZER; int i = 1; void* run(void *); void main(int argc, char **argv) { pthread_t pid1; pthread_t pid2; pthread_t pid3; pthread_t pid4; pthread_create(&pid1, NULL, run, NULL ); printf("new thread:%u\n", (unsigned int)pid1); sleep(1); pthread_create(&pid2, NULL, run, NULL ); printf("new thread:%u\n", (unsigned int)pid2); sleep(1); pthread_create(&pid3, NULL, run, NULL ); printf("new thread:%u\n", (unsigned int)pid3); sleep(1); pthread_create(&pid4, NULL, run, NULL ); printf("new thread:%u\n", (unsigned int)pid4); sleep(1); //修改 pthread_mutex_lock(&mutex); i = 2; pthread_cond_signal(&cond_l); printf("release signal\n"); sleep(1); i = 2; pthread_cond_signal(&cond_l); printf("release signal\n"); sleep(1); pthread_join(pid1, NULL ); pthread_join(pid2, NULL ); pthread_join(pid3, NULL ); pthread_join(pid4, NULL ); } void* run(void *s) { pthread_mutex_lock(&mutex); while(i == 1) { printf("線程%u進入等待狀態\n", (unsigned int)pthread_self()); pthread_cond_wait(&cond_l, &mutex); } printf("已經解開%u\n", (unsigned int)pthread_self()); pthread_mutex_unlock(&mutex); i = 1; return ((void *) 1); }
注意帶注釋的地方,在執行pthread_cond_signal之前,我又把互斥鎖鎖住了。之所以這里敢這么寫,是因為其他幾個子線程最后卡在pthread_cond_wait的時候都會把鎖給釋放掉的,所以我能在主線程里取得互斥鎖。這樣的話,其他子線程接到條件滿足的信號后還會從等待中跳出來嗎?運行結果如下:
new thread:3085290400
線程3085290400進入等待狀態
new thread:3076897696
線程3076897696進入等待狀態
new thread:3068504992
線程3068504992進入等待狀態
new thread:3060112288
線程3060112288進入等待狀態
release signal
release signal
Oh,No,果然,沒有一個線程跑出來。事實上,如果不是這么改,而是讓每個線程在run函數最后不釋放互斥鎖,最后只會有第一個跑出來的線程解套成功。所以,從目前來看,我的分析應該是正確的。
