Linux多線程編程詳細解析----條件變量 pthread_cond_t


Linux操作系統下的多線程編程詳細解析----條件變量

 

1.初始化條件變量pthread_cond_init

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *cv,

const pthread_condattr_t *cattr);

返回值:函數成功返回0;任何其他返回值都表示錯誤

初始化一個條件變量。當參數cattr為空指針時,函數創建的是一個缺省的條件變量。否則條件變量的屬性將由cattr中的屬性值來決定。調用 pthread_cond_init函數時,參數cattr為空指針等價於cattr中的屬性為缺省屬性,只是前者不需要cattr所占用的內存開銷。這個函數返回時,條件變量被存放在參數cv指向的內存中。

可以用宏PTHREAD_COND_INITIALIZER來初始化靜態定義的條件變量,使其具有缺省屬性。這和用pthread_cond_init函數動態分配的效果是一樣的。初始化時不進行錯誤檢查。如:

pthread_cond_t cv = PTHREAD_COND_INITIALIZER;

不能由多個線程同時初始化一個條件變量。當需要重新初始化或釋放一個條件變量時,應用程序必須保證這個條件變量未被使用。

 

2.阻塞在條件變量上pthread_cond_wait

#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *cv,

pthread_mutex_t *mutex);

返回值:函數成功返回0;任何其他返回值都表示錯誤

函數將解鎖mutex參數指向的互斥鎖,並使當前線程阻塞在cv參數指向的條件變量上。

被阻塞的線程可以被pthread_cond_signal函數,pthread_cond_broadcast函數喚醒,也可能在被信號中斷后被喚醒。

pthread_cond_wait函數的返回並不意味着條件的值一定發生了變化,必須重新檢查條件的值。

pthread_cond_wait函數返回時,相應的互斥鎖將被當前線程鎖定,即使是函數出錯返回。

一般一個條件表達式都是在一個互斥鎖的保護下被檢查。當條件表達式未被滿足時,線程將仍然阻塞在這個條件變量上。當另一個線程改變了條件的值並向條件變量發出信號時,等待在這個條件變量上的一個線程或所有線程被喚醒,接着都試圖再次占有相應的互斥鎖。

阻塞在條件變量上的線程被喚醒以后,直到pthread_cond_wait()函數返回之前條件的值都有可能發生變化。所以函數返回以后,在鎖定相應的互斥鎖之前,必須重新測試條件值。最好的測試方法是循環調用pthread_cond_wait函數,並把滿足條件的表達式置為循環的終止條件。如:

pthread_mutex_lock();

while (condition_is_false)

pthread_cond_wait();

pthread_mutex_unlock();

阻塞在同一個條件變量上的不同線程被釋放的次序是不一定的。

注意:pthread_cond_wait()函數是退出點,如果在調用這個函數時,已有一個掛起的退出請求,且線程允許退出,這個線程將被終止並開始執行善后處理函數,而這時和條件變量相關的互斥鎖仍將處在鎖定狀態。

 

3.解除在條件變量上的阻塞pthread_cond_signal

#include <pthread.h>

int pthread_cond_signal(pthread_cond_t *cv);

返回值:函數成功返回0;任何其他返回值都表示錯誤

函數被用來釋放被阻塞在指定條件變量上的一個線程。

必須在互斥鎖的保護下使用相應的條件變量。否則對條件變量的解鎖有可能發生在鎖定條件變量之前,從而造成死鎖。

喚醒阻塞在條件變量上的所有線程的順序由調度策略決定,如果線程的調度策略是SCHED_OTHER類型的,系統將根據線程的優先級喚醒線程。

如果沒有線程被阻塞在條件變量上,那么調用pthread_cond_signal()將沒有作用。

 

4.阻塞直到指定時間pthread_cond_timedwait

#include <pthread.h>

#include <time.h>

int pthread_cond_timedwait(pthread_cond_t *cv,

pthread_mutex_t *mp, const structtimespec * abstime);

返回值:函數成功返回0;任何其他返回值都表示錯誤

函數到了一定的時間,即使條件未發生也會解除阻塞。這個時間由參數abstime指定。函數返回時,相應的互斥鎖往往是鎖定的,即使是函數出錯返回。

注意:pthread_cond_timedwait函數也是退出點。

超時時間參數是指一天中的某個時刻。使用舉例:

pthread_timestruc_t to;

to.tv_sec = time(NULL) + TIMEOUT;

to.tv_nsec = 0;

超時返回的錯誤碼是ETIMEDOUT。

 

5.釋放阻塞的所有線程pthread_cond_broadcast

#include <pthread.h>

int pthread_cond_broadcast(pthread_cond_t *cv);

返回值:函數成功返回0;任何其他返回值都表示錯誤

函數喚醒所有被pthread_cond_wait函數阻塞在某個條件變量上的線程,參數cv被用來指定這個條件變量。當沒有線程阻塞在這個條件變量上時,pthread_cond_broadcast函數無效。

由於pthread_cond_broadcast函數喚醒所有阻塞在某個條件變量上的線程,這些線程被喚醒后將再次競爭相應的互斥鎖,所以必須小心使用pthread_cond_broadcast函數。

 

6.釋放條件變量pthread_cond_destroy

#include <pthread.h>

int pthread_cond_destroy(pthread_cond_t *cv);

返回值:函數成功返回0;任何其他返回值都表示錯誤

釋放條件變量。

注意:條件變量占用的空間並未被釋放。

 

7.喚醒丟失問題

在線程未獲得相應的互斥鎖時調用pthread_cond_signal或pthread_cond_broadcast函數可能會引起喚醒丟失問題。

喚醒丟失往往會在下面的情況下發生:

  1. 一個線程調用pthread_cond_signal或pthread_cond_broadcast函數;
  2. 另一個線程正處在測試條件變量和調用pthread_cond_wait函數之間;
  3. 沒有線程正在處在阻塞等待的狀態下。

 

 

轉載聲明: 本文轉自 http://pzs1237.blog.163.com/blog/static/29813006200952335454934/


 

===============================================================================

 

條件鎖pthread_cond_t

 

 

說明,
等待線程
1。使用pthread_cond_wait前要先加鎖
2。pthread_cond_wait內部會解鎖,然后等待條件變量被其它線程激活
3。pthread_cond_wait被激活后會再自動加鎖

激活線程:
1。加鎖(和等待線程用同一個鎖)
2。pthread_cond_signal發送信號
3。解鎖
激活線程的上面三個操作在運行時間上都在等待線程的pthread_cond_wait函數內部。

程序示例:

 1     #include <stdio.h>  
 2     #include <pthread.h>  
 3     #include <unistd.h>  
 4       
 5     pthread_mutex_t count_lock;  
 6     pthread_cond_t count_nonzero;  
 7     unsigned count = 0;  
 8       
 9     void *decrement_count(void *arg)  
10     {  
11         pthread_mutex_lock(&count_lock);  
12         printf("decrement_count get count_lock/n");  
13         while(count == 0)  
14         {  
15             printf("decrement_count count == 0 /n");  
16             printf("decrement_count before cond_wait /n");  
17             pthread_cond_wait(&count_nonzero, &count_lock);  
18             printf("decrement_count after cond_wait /n");  
19         }  
20       
21         count = count + 1;  
22         pthread_mutex_unlock(&count_lock);  
23     }  
24       
25     void *increment_count(void *arg)  
26     {  
27         pthread_mutex_lock(&count_lock);  
28         printf("increment_count get count_lock /n");  
29         if(count == 0)  
30         {  
31             printf("increment_count before cond_signal /n");  
32             pthread_cond_signal(&count_nonzero);  
33             printf("increment_count after cond_signal /n");  
34         }  
35       
36         count = count + 1;  
37         pthread_mutex_unlock(&count_lock);  
38     }  
39       
40     int main(void)  
41     {  
42         pthread_t tid1, tid2;  
43       
44         pthread_mutex_init(&count_lock, NULL);  
45         pthread_cond_init(&count_nonzero, NULL);  
46       
47         pthread_create(&tid1, NULL, decrement_count, NULL);  
48         sleep(2);  
49         pthread_create(&tid2, NULL, increment_count, NULL);  
50       
51         sleep(10);  
52         pthread_exit(0);  
53       
54         return 0;  
55     }  

 

運行結果:

[work@db-testing-com06-vm3.db01.baidu.com pthread]$ gcc -o pthread_cond pthread_cond.c -lpthread
[work@db-testing-com06-vm3.db01.baidu.com pthread]$ ./pthread_cond 
decrement_count get count_lock
decrement_count count == 0 
decrement_count before cond_wait 
increment_count get count_lock 
increment_count before cond_signal 
increment_count after cond_signal 
decrement_count after cond_wait


轉載聲明: 本文轉自 http://egeho123.blogbus.com/logs/10821816.html


 

===============================================================================

 

多線程編程,條件變量pthread_cond_t應用

 

程序代碼:

 1     #include <stdio.h>  
 2     #include <pthread.h>  
 3     #include <unistd.h>  
 4       
 5     pthread_mutex_t counter_lock;  
 6     pthread_cond_t counter_nonzero;  
 7     int counter = 0;  
 8     int estatus = -1;  
 9       
10     void *decrement_counter(void *argv);  
11     void *increment_counter(void *argv);  
12       
13     int main(int argc, char **argv)  
14     {  
15         printf("counter: %d/n", counter);  
16         pthread_t thd1, thd2;  
17         int ret;  
18       
19         ret = pthread_create(&thd1, NULL, decrement_counter, NULL);  
20         if(ret){  
21             perror("del:/n");  
22             return 1;  
23         }  
24       
25         ret = pthread_create(&thd2, NULL, increment_counter, NULL);  
26         if(ret){  
27             perror("inc: /n");  
28             return 1;  
29         }  
30       
31         int counter = 0;  
32         while(counter != 10){  
33             printf("counter: %d/n", counter);  
34             sleep(1);  
35             counter++;  
36         }  
37       
38         return 0;  
39     }  
40       
41     void *decrement_counter(void *argv)  
42     {  
43         pthread_mutex_lock(&counter_lock);  
44         while(counter == 0)  
45             pthread_cond_wait(&counter_nonzero, &counter_lock);  
46       
47         counter--;  
48         pthread_mutex_unlock(&counter_lock);  
49       
50         return &estatus;  
51     }  
52       
53     void *increment_counter(void *argv)  
54     {  
55         pthread_mutex_lock(&counter_lock);  
56         if(counter == 0)  
57             pthread_cond_signal(&counter_nonzero);  
58       
59         counter++;  
60         pthread_mutex_unlock(&counter_lock);  
61       
62         return &estatus;  
63     }  

運行結果:
[work@db-testing-com06-vm3.db01.baidu.com pthread]$ gcc -o pthread_cond2 pthread_cond2.c -lpthread
[work@db-testing-com06-vm3.db01.baidu.com pthread]$ ./pthread_cond2                               
counter: 0
counter: 0
counter: 1
counter: 2
counter: 3
counter: 4
counter: 5
counter: 6
counter: 7
counter: 8
counter: 9

 

 

 

調試程序的運行過程:

1、開始時  counter 為0 (main)
2、ret = pthread_create(&thrd1, NULL, decrement_counter, NULL)處生成一個thrd1線程運行decrement_counter(),
此線程內函數運行流程為:
先鎖定 互斥鎖(count_lock) 如果 counter為0,此線程被阻塞在 條件變量(count_nonzero)上.同時釋放互斥鎖count_lock(wait內部會先釋放鎖,等待signal激活后自動再加上鎖).

 

3、與此同時主程序還在運行,創建另一個線程thrd2運行 increment_counter,
此線程內的函數流程如下:
先鎖定 互斥鎖(count_lock)【 wait內部釋放鎖的互斥鎖】 如果counter為0, 喚醒在條件變量(count_nonzero)上的線程即thrd1.但是由於有互斥鎖count_lock【signal激活后,wait內部又自動加上鎖了】, thrd1還是在等待. 然后count++,釋放互斥鎖,.......thrd1由於互斥鎖釋放,重新判斷counter是不是為0,如果為0再把線程阻塞在條件變量count_nonzero上,但這時counter已經為1了.所以線程繼續運行.counter--釋放互斥鎖......(退出后,運行主線程main
4、與此主程序間隔打印counter運行一段時間退出.

 注:更清晰的運行流程請詳見如下“改進代碼”

后記,在編譯的時候加上 -lpthread
改進代碼:
 1     #include <stdio.h>  
 2     #include <pthread.h>  
 3     #include <unistd.h>  
 4       
 5     pthread_mutex_t counter_lock;  
 6     pthread_cond_t counter_nonzero;  
 7     int counter = 0;  
 8     int estatus = -1;  
 9       
10     void *decrement_counter(void *argv);  
11     void *increment_counter(void *argv);  
12       
13     int main(int argc, char **argv)  
14     {  
15         printf("counter: %d/n", counter);  
16         pthread_t thd1, thd2;  
17         int ret;  
18       
19         ret = pthread_create(&thd1, NULL, decrement_counter, NULL);  
20         if(ret){  
21             perror("del:/n");  
22             return 1;  
23         }  
24       
25         ret = pthread_create(&thd2, NULL, increment_counter, NULL);  
26         if(ret){  
27             perror("inc: /n");  
28             return 1;  
29         }  
30       
31         int counter = 0;  
32         while(counter != 10){  
33             printf("counter(main): %d/n", counter);  
34             sleep(1);  
35             counter++;  
36         }  
37       
38         return 0;  
39     }  
40       
41     void *decrement_counter(void *argv)  
42     {  
43         printf("counter(decrement): %d/n", counter);  
44         pthread_mutex_lock(&counter_lock);  
45         while(counter == 0)  
46             pthread_cond_wait(&counter_nonzero, &counter_lock); //進入阻塞(wait),等待激活(signal)  
47           
48         printf("counter--(before): %d/n", counter);      
49         counter--; //等待signal激活后再執行  
50         printf("counter--(after): %d/n", counter);      
51         pthread_mutex_unlock(&counter_lock);   
52       
53         return &estatus;  
54     }  
55       
56     void *increment_counter(void *argv)  
57     {  
58         printf("counter(increment): %d/n", counter);  
59         pthread_mutex_lock(&counter_lock);  
60         if(counter == 0)  
61             pthread_cond_signal(&counter_nonzero); //激活(signal)阻塞(wait)的線程(先執行完signal線程,然后再執行wait線程)  
62       
63         printf("counter++(before): %d/n", counter);      
64         counter++;   
65         printf("counter++(after): %d/n", counter);      
66         pthread_mutex_unlock(&counter_lock);  
67       
68         return &estatus;  
69     }  
運行結果:
[work@db-testing-com06-vm3.db01.baidu.com pthread]$ gcc -o pthread_cond2 pthread_cond2.c -lpthread
[work@db-testing-com06-vm3.db01.baidu.com pthread]$ ./pthread_cond2                               
counter: 0
counter(main): 0
counter(decrement): 0
counter(increment): 0
counter++(before): 0
counter++(after): 1
counter--(before): 1
counter--(after): 0
counter(main): 1
counter(main): 2
counter(main): 3
counter(main): 4
counter(main): 5
counter(main): 6
counter(main): 7
counter(main): 8
counter(main): 9
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM