在多線程編程下,常常出現(xiàn)A線程要等待B線程條件完成后再繼續(xù)進(jìn)行,這里等待方式有兩種:
1.使用鎖+輪詢
使用這種方法可以很簡(jiǎn)單的實(shí)現(xiàn),但是會(huì)有一定的性能消耗,其還有一個(gè)點(diǎn)要好好把握,就是一次輪詢沒有結(jié)果后相隔多久進(jìn)行下一次的輪詢,間隔時(shí)間太短,消耗的CPU資源較多,間隔時(shí)間太長(zhǎng),不能很及時(shí)的響應(yīng)請(qǐng)求。
所以這種方法不是推薦。
2.使用條件變量的線程同步(推薦)
采用阻塞和消息方式可以極大程度上減少資源的浪費(fèi)以及增加實(shí)時(shí)性
線程條件變量pthread_cond_t
線程等待某個(gè)條件
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);?
只通知一個(gè)線程
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; ?//此操作是帶鎖的,也就是說只有一個(gè)線程同時(shí)進(jìn)入這塊
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;任何其他返回值都表示錯(cuò)誤
初始化一個(gè)條件變量。當(dāng)參數(shù)cattr為空指針時(shí),函數(shù)創(chuàng)建的是一個(gè)缺省的條件變量。否則條件變量的屬性將由cattr中的屬性值來決定。調(diào)用 pthread_cond_init函數(shù)時(shí),參數(shù)cattr為空指針等價(jià)于cattr中的屬性為缺省屬性,只是前者不需要cattr所占用的內(nèi)存開銷。這個(gè)函數(shù)返回時(shí),條件變量被存放在參數(shù)cv指向的內(nèi)存中。
可以用宏P(guān)THREAD_COND_INITIALIZER來初始化靜態(tài)定義的條件變量,使其具有缺省屬性。這和用pthread_cond_init函數(shù)動(dòng)態(tài)分配的效果是一樣的。初始化時(shí)不進(jìn)行錯(cuò)誤檢查。如:
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
不能由多個(gè)線程同時(shí)初始化一個(gè)條件變量。當(dāng)需要重新初始化或釋放一個(gè)條件變量時(shí),應(yīng)用程序必須保證這個(gè)條件變量未被使用。
2.阻塞在條件變量上pthread_cond_wait
#include
int pthread_cond_wait(pthread_cond_t *cv,
pthread_mutex_t *mutex);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯(cuò)誤
函數(shù)將解鎖mutex參數(shù)指向的互斥鎖,并使當(dāng)前線程阻塞在cv參數(shù)指向的條件變量上。
被阻塞的線程可以被pthread_cond_signal函數(shù),pthread_cond_broadcast函數(shù)喚醒,也可能在被信號(hào)中斷后被喚醒。
pthread_cond_wait函數(shù)的返回并不意味著條件的值一定發(fā)生了變化,必須重新檢查條件的值。
pthread_cond_wait函數(shù)返回時(shí),相應(yīng)的互斥鎖將被當(dāng)前線程鎖定,即使是函數(shù)出錯(cuò)返回。
一般一個(gè)條件表達(dá)式都是在一個(gè)互斥鎖的保護(hù)下被檢查。當(dāng)條件表達(dá)式未被滿足時(shí),線程將仍然阻塞在這個(gè)條件變量上。當(dāng)另一個(gè)線程改變了條件的值并向條件變量發(fā)出信號(hào)時(shí),等待在這個(gè)條件變量上的一個(gè)線程或所有線程被喚醒,接著都試圖再次占有相應(yīng)的互斥鎖。
阻塞在條件變量上的線程被喚醒以后,直到pthread_cond_wait()函數(shù)返回之前條件的值都有可能發(fā)生變化。所以函數(shù)返回以后,在鎖定相應(yīng)的互斥鎖之前,必須重新測(cè)試條件值。最好的測(cè)試方法是循環(huán)調(diào)用pthread_cond_wait函數(shù),并把滿足條件的表達(dá)式置為循環(huán)的終止條件。如:
pthread_mutex_lock();
while (condition_is_false)
pthread_cond_wait();
pthread_mutex_unlock();
阻塞在同一個(gè)條件變量上的不同線程被釋放的次序是不一定的。
注意:pthread_cond_wait()函數(shù)是退出點(diǎn),如果在調(diào)用這個(gè)函數(shù)時(shí),已有一個(gè)掛起的退出請(qǐng)求,且線程允許退出,這個(gè)線程將被終止并開始執(zhí)行善后處理函數(shù),而這時(shí)和條件變量相關(guān)的互斥鎖仍將處在鎖定狀態(tài)。
3.解除在條件變量上的阻塞pthread_cond_signal
#include
int pthread_cond_signal(pthread_cond_t *cv);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯(cuò)誤
函數(shù)被用來釋放被阻塞在指定條件變量上的一個(gè)線程。
必須在互斥鎖的保護(hù)下使用相應(yīng)的條件變量。否則對(duì)條件變量的解鎖有可能發(fā)生在鎖定條件變量之前,從而造成死鎖。
喚醒阻塞在條件變量上的所有線程的順序由調(diào)度策略決定,如果線程的調(diào)度策略是SCHED_OTHER類型的,系統(tǒng)將根據(jù)線程的優(yōu)先級(jí)喚醒線程。
如果沒有線程被阻塞在條件變量上,那么調(diào)用pthread_cond_signal()將沒有作用。
4.阻塞直到指定時(shí)間pthread_cond_timedwait
#include
#include
int pthread_cond_timedwait(pthread_cond_t *cv,
pthread_mutex_t *mp, const structtimespec * abstime);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯(cuò)誤
函數(shù)到了一定的時(shí)間,即使條件未發(fā)生也會(huì)解除阻塞。這個(gè)時(shí)間由參數(shù)abstime指定。函數(shù)返回時(shí),相應(yīng)的互斥鎖往往是鎖定的,即使是函數(shù)出錯(cuò)返回。
注意:pthread_cond_timedwait函數(shù)也是退出點(diǎn)。
超時(shí)時(shí)間參數(shù)是指一天中的某個(gè)時(shí)刻。使用舉例:
pthread_timestruc_t to;
to.tv_sec = time(NULL) + TIMEOUT;
to.tv_nsec = 0;
超時(shí)返回的錯(cuò)誤碼是ETIMEDOUT。
5.釋放阻塞的所有線程pthread_cond_broadcast
#include
int pthread_cond_broadcast(pthread_cond_t *cv);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯(cuò)誤
函數(shù)喚醒所有被pthread_cond_wait函數(shù)阻塞在某個(gè)條件變量上的線程,參數(shù)cv被用來指定這個(gè)條件變量。當(dāng)沒有線程阻塞在這個(gè)條件變量上時(shí),pthread_cond_broadcast函數(shù)無效。
由于pthread_cond_broadcast函數(shù)喚醒所有阻塞在某個(gè)條件變量上的線程,這些線程被喚醒后將再次競(jìng)爭(zhēng)相應(yīng)的互斥鎖,所以必須小心使用pthread_cond_broadcast函數(shù)。
6.釋放條件變量pthread_cond_destroy
#include
int pthread_cond_destroy(pthread_cond_t *cv);
返回值:函數(shù)成功返回0;任何其他返回值都表示錯(cuò)誤
釋放條件變量。
注意:條件變量占用的空間并未被釋放。
7.喚醒丟失問題
在線程未獲得相應(yīng)的互斥鎖時(shí)調(diào)用pthread_cond_signal或pthread_cond_broadcast函數(shù)可能會(huì)引起喚醒丟失問題。
喚醒丟失往往會(huì)在下面的情況下發(fā)生:
一個(gè)線程調(diào)用pthread_cond_signal或pthread_cond_broadcast函數(shù);
另一個(gè)線程正處在測(cè)試條件變量和調(diào)用pthread_cond_wait函數(shù)之間;
沒有線程正在處在阻塞等待的狀態(tài)下
最后是兩個(gè)可以直接運(yùn)行的程序,可以更好的理解條件變量的運(yùn)行過程。
(注意:為了驗(yàn)證整個(gè)程序的運(yùn)行過程,添加了多條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;??}??
/*條件變量
互斥鎖一個(gè)明顯的缺點(diǎn)是它只有兩種狀態(tài):鎖定和非鎖定。而條件變量通過允許線程阻塞和等待另一個(gè)線程發(fā)送信號(hào)的方法彌補(bǔ)了互斥鎖的不足,
它常和互斥鎖一起使用。使用時(shí),條件變量被用來阻塞一個(gè)線程,當(dāng)條件不滿足時(shí),線程往往解開相應(yīng)的互斥鎖并等待條件發(fā)生變化。一旦其它的
某個(gè)線程改變了條件變量,它將通知相應(yīng)的條件變量喚醒一個(gè)或多個(gè)正被此條件變量阻塞的線程。這些線程將重新鎖定互斥鎖并重新測(cè)試條件是否
滿足。一般說來,條件變量被用來進(jìn)行線程間的同步。
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內(nèi)部會(huì)解鎖,然后等待條件變量被其它線程激活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加鎖(和等待線程用同一個(gè)鎖)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ā)送信號(hào)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解鎖.激活線程的上面三個(gè)操作在運(yùn)行時(shí)間上都在等待線程的pthread_cond_wait函數(shù)內(nèi)部。
}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;
}
第二個(gè)例程:
[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;??}??
總結(jié)
以上是生活随笔為你收集整理的pthread_cond_t的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。