在多線程編程下,常常出現(xiàn)A線程要等待B線程條件完成后再繼續(xù)進行,這里等待方式有兩種:
1.使用鎖+輪詢
使用這種方法可以很簡單的實現(xiàn),但是會有一定的性能消耗,其還有一個點要好好把握,就是一次輪詢沒有結果后相隔多久進行下一次的輪詢,間隔時間太短,消耗的CPU資源較多,間隔時間太長,不能很及時的響應請求。
所以這種方法不是推薦。
2.使用條件變量的線程同步(推薦)
采用阻塞和消息方式可以極大程度上減少資源的浪費以及增加實時性
線程條件變量pthread_cond_t
線程等待某個條件
int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);?
通知函數(shù)
通知所有的線程
int pthread_cond_broadcast(pthread_cond_t *cond);?
只通知一個線程
int pthread_cond_signal(pthread_cond_t *cond);?
---------------------------------------------------------------------------------
正確的使用方法
?pthread_cond_wait用法:
pthread_mutex_lock(&mutex);
while(condition_is_false)
??{
?pthread_cond_wait(&cond,&mutex);
?}
condition_is_false=true; ?//此操作是帶鎖的,也就是說只有一個線程同時進入這塊
pthread_mutex_unlock(&mutex);
?----------------------------------------------------
pthread_cond_signal用法:?
pthread_mutex_lock(&mutex);
condition_is_false=false;
pthread_cond_signal(&cond)
pthread_mutex_unlock(&mutex)
--------------------------------------------------------
?記住上面這種用法!!!可以避免誤用pthread_cond_broadcast而釋放了所有條件變量
下面是pthread_cond_XX的用法
1.初始化條件變量pthread_cond_init
#include
int pthread_cond_init(pthread_cond_t *cv,
const pthread_condattr_t *cattr);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯誤
初始化一個條件變量。當參數(shù)cattr為空指針時,函數(shù)創(chuàng)建的是一個缺省的條件變量。否則條件變量的屬性將由cattr中的屬性值來決定。調用 pthread_cond_init函數(shù)時,參數(shù)cattr為空指針等價于cattr中的屬性為缺省屬性,只是前者不需要cattr所占用的內存開銷。這個函數(shù)返回時,條件變量被存放在參數(shù)cv指向的內存中。
可以用宏PTHREAD_COND_INITIALIZER來初始化靜態(tài)定義的條件變量,使其具有缺省屬性。這和用pthread_cond_init函數(shù)動態(tài)分配的效果是一樣的。初始化時不進行錯誤檢查。如:
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
不能由多個線程同時初始化一個條件變量。當需要重新初始化或釋放一個條件變量時,應用程序必須保證這個條件變量未被使用。
2.阻塞在條件變量上pthread_cond_wait
#include
int pthread_cond_wait(pthread_cond_t *cv,
pthread_mutex_t *mutex);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯誤
函數(shù)將解鎖mutex參數(shù)指向的互斥鎖,并使當前線程阻塞在cv參數(shù)指向的條件變量上。
被阻塞的線程可以被pthread_cond_signal函數(shù),pthread_cond_broadcast函數(shù)喚醒,也可能在被信號中斷后被喚醒。
pthread_cond_wait函數(shù)的返回并不意味著條件的值一定發(fā)生了變化,必須重新檢查條件的值。
pthread_cond_wait函數(shù)返回時,相應的互斥鎖將被當前線程鎖定,即使是函數(shù)出錯返回。
一般一個條件表達式都是在一個互斥鎖的保護下被檢查。當條件表達式未被滿足時,線程將仍然阻塞在這個條件變量上。當另一個線程改變了條件的值并向條件變量發(fā)出信號時,等待在這個條件變量上的一個線程或所有線程被喚醒,接著都試圖再次占有相應的互斥鎖。
阻塞在條件變量上的線程被喚醒以后,直到pthread_cond_wait()函數(shù)返回之前條件的值都有可能發(fā)生變化。所以函數(shù)返回以后,在鎖定相應的互斥鎖之前,必須重新測試條件值。最好的測試方法是循環(huán)調用pthread_cond_wait函數(shù),并把滿足條件的表達式置為循環(huán)的終止條件。如:
pthread_mutex_lock();
while (condition_is_false)
pthread_cond_wait();
pthread_mutex_unlock();
阻塞在同一個條件變量上的不同線程被釋放的次序是不一定的。
注意:pthread_cond_wait()函數(shù)是退出點,如果在調用這個函數(shù)時,已有一個掛起的退出請求,且線程允許退出,這個線程將被終止并開始執(zhí)行善后處理函數(shù),而這時和條件變量相關的互斥鎖仍將處在鎖定狀態(tài)。
3.解除在條件變量上的阻塞pthread_cond_signal
#include
int pthread_cond_signal(pthread_cond_t *cv);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯誤
函數(shù)被用來釋放被阻塞在指定條件變量上的一個線程。
必須在互斥鎖的保護下使用相應的條件變量。否則對條件變量的解鎖有可能發(fā)生在鎖定條件變量之前,從而造成死鎖。
喚醒阻塞在條件變量上的所有線程的順序由調度策略決定,如果線程的調度策略是SCHED_OTHER類型的,系統(tǒng)將根據(jù)線程的優(yōu)先級喚醒線程。
如果沒有線程被阻塞在條件變量上,那么調用pthread_cond_signal()將沒有作用。
4.阻塞直到指定時間pthread_cond_timedwait
#include
#include
int pthread_cond_timedwait(pthread_cond_t *cv,
pthread_mutex_t *mp, const structtimespec * abstime);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯誤
函數(shù)到了一定的時間,即使條件未發(fā)生也會解除阻塞。這個時間由參數(shù)abstime指定。函數(shù)返回時,相應的互斥鎖往往是鎖定的,即使是函數(shù)出錯返回。
注意:pthread_cond_timedwait函數(shù)也是退出點。
超時時間參數(shù)是指一天中的某個時刻。使用舉例:
pthread_timestruc_t to;
to.tv_sec = time(NULL) + TIMEOUT;
to.tv_nsec = 0;
超時返回的錯誤碼是ETIMEDOUT。
5.釋放阻塞的所有線程pthread_cond_broadcast
#include
int pthread_cond_broadcast(pthread_cond_t *cv);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯誤
函數(shù)喚醒所有被pthread_cond_wait函數(shù)阻塞在某個條件變量上的線程,參數(shù)cv被用來指定這個條件變量。當沒有線程阻塞在這個條件變量上時,pthread_cond_broadcast函數(shù)無效。
由于pthread_cond_broadcast函數(shù)喚醒所有阻塞在某個條件變量上的線程,這些線程被喚醒后將再次競爭相應的互斥鎖,所以必須小心使用pthread_cond_broadcast函數(shù)。
6.釋放條件變量pthread_cond_destroy
#include
int pthread_cond_destroy(pthread_cond_t *cv);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯誤
釋放條件變量。
注意:條件變量占用的空間并未被釋放。
7.喚醒丟失問題
在線程未獲得相應的互斥鎖時調用pthread_cond_signal或pthread_cond_broadcast函數(shù)可能會引起喚醒丟失問題。
喚醒丟失往往會在下面的情況下發(fā)生:
一個線程調用pthread_cond_signal或pthread_cond_broadcast函數(shù);
另一個線程正處在測試條件變量和調用pthread_cond_wait函數(shù)之間;
沒有線程正在處在阻塞等待的狀態(tài)下
最后是兩個可以直接運行的程序,可以更好的理解條件變量的運行過程。
(注意:為了驗證整個程序的運行過程,添加了多條printf語句)
[cpp] view plaincopy print?
???????????#include?<stdio.h>??#include?<pthread.h>??#include?<unistd.h>????pthread_mutex_t?count_lock;??pthread_cond_t?count_nonzero;??unsigned?count?=?0;????void?*decrement_count(void?*arg)??{??????pthread_mutex_lock(&count_lock);??????printf("decrement_count?get?count_lock\n");??????while(count?==?0)??????{??????????printf("decrement_count?count?==?0?\n");??????????printf("decrement_count?before?cond_wait?\n");??????????pthread_cond_wait(&count_nonzero,?&count_lock);??????????printf("decrement_count?after?cond_wait?\n");??????}??????printf("tid1:count--\n");??????count?=?count?-?1;??????printf("tid1:count=?%d?\n",?count);??????pthread_mutex_unlock(&count_lock);??}????void?*increment_count(void?*arg)??{??????pthread_mutex_lock(&count_lock);???????????????printf("increment_count?get?count_lock?\n");??????if(count?==?0)??????{??????????printf("increment_count?before?cond_signal?\n");??????????pthread_cond_signal(&count_nonzero);?????????????????????printf("increment_count?after?cond_signal?\n");??????}??printf("tid2:count++\n");??????count?=?count?+?1;??????printf("tid2:count=?%d?\n",?count);??????pthread_mutex_unlock(&count_lock);??}????int?main(void)??{??????pthread_t?tid1,?tid2;????????pthread_mutex_init(&count_lock,?NULL);??????pthread_cond_init(&count_nonzero,?NULL);????????pthread_create(&tid1,?NULL,?decrement_count,?NULL);??printf("tid1?decrement?is?created,begin?sleep?2s?\n");??????sleep(2);??printf("after?sleep?2s,?start?creat?tid2?increment?\n");??????pthread_create(&tid2,?NULL,?increment_count,?NULL);??printf("after?tid2?increment?is?created,begin?sleep?10s?\n");??????sleep(5);??printf("after?sleep?5s,begin?exit!\n");??????pthread_exit(0);????????return?0;??}??
/*條件變量
互斥鎖一個明顯的缺點是它只有兩種狀態(tài):鎖定和非鎖定。而條件變量通過允許線程阻塞和等待另一個線程發(fā)送信號的方法彌補了互斥鎖的不足,
它常和互斥鎖一起使用。使用時,條件變量被用來阻塞一個線程,當條件不滿足時,線程往往解開相應的互斥鎖并等待條件發(fā)生變化。一旦其它的
某個線程改變了條件變量,它將通知相應的條件變量喚醒一個或多個正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖并重新測試條件是否
滿足。一般說來,條件變量被用來進行線程間的同步。
cond1.c
[root@mashang smb]# gcc cond1.c -o cond1 -lpthread
[root@mashang smb]# ./cond1
*/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>pthread_mutex_t count_lock;//自旋鎖
pthread_cond_t count_nonzero;//條件鎖
unsigned count = 0;void *decrement_count(void *arg)
{pthread_mutex_lock(&count_lock);//等待線程:1使用pthread_cond_wait前要先加鎖printf("decrement_count get count_lock\n");while(count == 0){printf("decrement_count count == 0 \n");printf("decrement_count before cond_wait \n");pthread_cond_wait(&count_nonzero, &count_lock);//2pthread_cond_wait內部會解鎖,然后等待條件變量被其它線程激活printf("decrement_count after cond_wait \n");}printf("tid1:count--\n");count = count - 1;printf("tid1:count= %d \n", count);pthread_mutex_unlock(&count_lock);
}void *increment_count(void *arg)
{pthread_mutex_lock(&count_lock); //激活線程:1加鎖(和等待線程用同一個鎖)printf("increment_count get count_lock \n");if(count == 0){printf("increment_count before cond_signal \n");pthread_cond_signal(&count_nonzero); //2pthread_cond_signal發(fā)送信號printf("increment_count after cond_signal \n");}
printf("tid2:count++\n");count = count + 1;printf("tid2:count= %d \n", count);pthread_mutex_unlock(&count_lock);//3解鎖.激活線程的上面三個操作在運行時間上都在等待線程的pthread_cond_wait函數(shù)內部。
}int main(void)
{pthread_t tid1, tid2;pthread_mutex_init(&count_lock, NULL);pthread_cond_init(&count_nonzero, NULL);pthread_create(&tid1, NULL, decrement_count, NULL);
printf("tid1 decrement is created,begin sleep 2s \n");sleep(2);
printf("after sleep 2s, start creat tid2 increment \n");pthread_create(&tid2, NULL, increment_count, NULL);
printf("after tid2 increment is created,begin sleep 10s \n");sleep(5);
printf("after sleep 5s,begin exit!\n");pthread_exit(0);return 0;
}
第二個例程:
[cpp] view plaincopy print?
?????????????????????#include?<stdio.h>??#include?<pthread.h>??#include?<unistd.h>????pthread_mutex_t?counter_lock;??pthread_cond_t?counter_nonzero;??int?counter?=?0;??int?estatus?=?-1;????void?*decrement_counter(void?*argv);??void?*increment_counter(void?*argv);????int?main(int?argc,?char?**argv)??{??????printf("counter:?%d\n",?counter);??????pthread_t?thd1,?thd2;??????int?ret;??printf("main1:?before?creating?thd1?decrement?\n");??????ret?=?pthread_create(&thd1,?NULL,?decrement_counter,?NULL);??????if(ret){??????????perror("del:/n");??????????return?1;??????}??printf("main2:thd1?is?created,?begin?create?thd2?increment?\n");??????ret?=?pthread_create(&thd2,?NULL,?increment_counter,?NULL);??????if(ret){??????????perror("inc:?/n");??????????return?1;??????}??printf("main3:after?thd1?and?thd2,?begin?sleep?and?exit!\n");??????sleep(3);??????return?0;??}????void?*decrement_counter(void?*argv)??{??????pthread_mutex_lock(&counter_lock);??printf("thd1?decrement?get?the?lock?\n");??????while(counter?==?0)??????{???????printf("thd1?decrement?before?cond_wait\n");??????????pthread_cond_wait(&counter_nonzero,?&counter_lock);???????printf("thd1?decrement?after?cond_wait?\n");??????}??????counter--;??????printf("thd1?decrement:counter?=?%d?\n",?counter);??????pthread_mutex_unlock(&counter_lock);????????return?&estatus;??}????void?*increment_counter(void?*argv)??{??????pthread_mutex_lock(&counter_lock);??printf("thd2?increment?get?the?lock\n");??????if(counter?==?0)??????{???????printf("thd2?increment?before?cond_signal\n");???????pthread_cond_signal(&counter_nonzero);???????printf("thd2?increment?after??cond_signal\n");??????}??????counter++;??????printf("thd2?increment:counter?=?%d?\n",?counter);??????pthread_mutex_unlock(&counter_lock);????????return?&estatus;??}??
總結
以上是生活随笔為你收集整理的pthread_cond_t的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。