Linux多線程-互斥和同步

      網(wǎng)友投稿 822 2025-04-03

      @TOC

      零、前言

      本章主要講解學(xué)習(xí)linux中對多線程的執(zhí)行中的互斥與安全問題

      一、linux線程互斥

      1、基本概念及引入

      互斥相關(guān)概念:

      臨界資源:多線程執(zhí)行流共享的資源就叫做臨界資源

      臨界區(qū):每個(gè)線程內(nèi)部,訪問臨界資源的代碼,就叫做臨界區(qū)

      互斥:任何時(shí)刻,互斥保證有且只有一個(gè)執(zhí)行流進(jìn)入臨界區(qū),訪問臨界資源,通常對臨界資源起保護(hù)作用

      原子性:不會被任何調(diào)度機(jī)制打斷的操作,該操作只有兩態(tài),要么完成,要么未完成

      示例:模擬搶票

      #include #include #include int thickets=100;//100張票 //thickets--表示搶票 void* Routine(void* arg) { while(1) { if(thickets>0) { usleep(30000);//搶票時(shí)間 printf("%s get a thickets, now thickets' number:%d\n",(char*)arg,--thickets); } else break; } return NULL; } int main() { pthread_t tid1,tid2,tid3; pthread_create(&tid1,NULL,Routine,(void*)"thread 1"); pthread_create(&tid2,NULL,Routine,(void*)"thread 2"); pthread_create(&tid3,NULL,Routine,(void*)"thread 3"); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); return 0; }

      效果:

      注:變量tickets被多個(gè)執(zhí)行流同時(shí)訪問,所以thickets就是一個(gè)臨界資源,當(dāng)訪問臨界資源時(shí),判斷tickets是否大于0、打印剩余票數(shù)以及--tickets的代碼也就是臨界區(qū)

      出現(xiàn)負(fù)數(shù)的原因:

      if語句判斷條件為真以后,代碼可以并發(fā)的切換到其他線程

      usleep用于模擬漫長業(yè)務(wù)的過程,在這個(gè)漫長的業(yè)務(wù)過程中,可能有很多個(gè)線程會進(jìn)入該代碼段

      –ticket操作本身就不是一個(gè)原子操作,可能在執(zhí)行當(dāng)中也被切換成其他線程

      具體可能的過程:

      當(dāng)thickets為1時(shí),一個(gè)線程進(jìn)行if判斷為真,進(jìn)入代碼段,當(dāng)執(zhí)行到usleep進(jìn)行系統(tǒng)調(diào)用休眠,返回時(shí)到用戶態(tài)時(shí)線程發(fā)生切換,多個(gè)線程此時(shí)也進(jìn)行if判斷為真(thickets還是1),這些線程當(dāng)進(jìn)行打印的時(shí)候進(jìn)行了多次的減減操作,也就造成了負(fù)數(shù)的情況

      – 操作并不是原子操作,而是對應(yīng)三條匯編指令:

      load :將共享變量ticket從內(nèi)存加載到寄存器中

      update : 更新寄存器里面的值,執(zhí)行-1操作

      store :將新值,從寄存器寫回共享變量ticket的內(nèi)存地址

      –執(zhí)行對應(yīng)的匯編代碼:

      152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax # 600b34 153 400651: 83 e8 01 sub

      152 40064b: 8b 05 e3 04 20 00 mov 0x2004e3(%rip),%eax # 600b34 153 400651: 83 e8 01 sub $0x1,%eax 154 400654: 89 05 da 04 20 00 mov %eax,0x2004da(%rip) # 600b34

      x1,%eax 154 400654: 89 05 da 04 20 00 mov %eax,0x2004da(%rip) # 600b34

      注:因?yàn)闇p減操作并不是原子的,當(dāng)減減操作第一步執(zhí)行完(thickets=100),可能該線程的時(shí)間片到了(寄存器中的數(shù)據(jù)被保存eax=100),其他線程切入,而切入的線程執(zhí)行了多次減減并寫會到內(nèi)存(thickets=80),當(dāng)切出的線程切回時(shí),恢復(fù)線程上下文數(shù)據(jù)(eax=100),再進(jìn)行減減(eax=99),把數(shù)據(jù)寫回到內(nèi)存時(shí)(thickets=99),此時(shí)的數(shù)據(jù)的值只達(dá)到了一次減減的效果,此時(shí)的資源并不安全

      2、互斥量mutex介紹

      概念:

      大部分情況,線程使用的數(shù)據(jù)都是局部變量,變量的地址空間在線程棧空間內(nèi),這種情況變量歸屬單個(gè)線程,其他線程無法獲得這種變量

      但有時(shí)候,很多變量都需要在線程間共享,這樣的變量成為共享變量,可以通過數(shù)據(jù)的共享,完成線程之間的交互

      多個(gè)線程并發(fā)的操作共享變量,就會帶來一些問題

      要解決以上問題需要做到三點(diǎn):

      代碼必須要有互斥行為:當(dāng)代碼進(jìn)入臨界區(qū)執(zhí)行時(shí),不允許其他線程進(jìn)入該臨界區(qū)

      Linux多線程-互斥和同步

      如果多個(gè)線程同時(shí)要求執(zhí)行臨界區(qū)的代碼,并且臨界區(qū)沒有線程在執(zhí)行,那么只能允許一個(gè)線程進(jìn)入該臨界區(qū)

      如果線程不在臨界區(qū)中執(zhí)行,那么該線程不能阻止其他線程進(jìn)入臨界區(qū)

      注:要做到這三點(diǎn),本質(zhì)上就是需要一把鎖,Linux上提供的這把鎖叫互斥量

      示圖:

      3、互斥量的使用

      初始化互斥量:

      靜態(tài)分配

      pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER

      動(dòng)態(tài)分配

      int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrictattr);

      參數(shù):mutex:要初始化的互斥量;attr:互斥量的屬性,一般設(shè)置為NULL

      銷毀互斥量:

      int pthread_mutex_destroy(pthread_mutex_t *mutex);

      注意:

      使用 PTHREAD_ MUTEX_ INITIALIZER 初始化的互斥量不需要銷毀

      不要銷毀一個(gè)已經(jīng)加鎖的互斥量

      已經(jīng)銷毀的互斥量,要確保后面不會有線程再嘗試加鎖

      互斥量加鎖和解鎖:

      int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);

      返回值:成功返回0,失敗返回錯(cuò)誤號

      調(diào)用 pthread_ lock 時(shí)可能遇到的情況:

      互斥量處于未鎖狀態(tài),該函數(shù)會將互斥量鎖定,同時(shí)返回成功

      發(fā)起函數(shù)調(diào)用時(shí),其他線程已經(jīng)鎖定互斥量,或者存在其他線程同時(shí)申請互斥量,但沒有競爭到互斥量,那么pthread_ lock調(diào)用會陷入阻塞(執(zhí)行流被掛起),等待互斥量解鎖

      示例:改進(jìn)搶票

      #include #include #include int thickets=100;//100張票 //thickets--表示搶票 pthread_mutex_t lock;//線程共用一個(gè)互斥鎖 void* Routine(void* arg) { while(1) { pthread_mutex_lock(&lock); if(thickets>0) { usleep(100000);//搶票時(shí)間 printf("%s get a thickets, now thickets' number:%d\n",(char*)arg,--thickets); pthread_mutex_unlock(&lock); } else { pthread_mutex_unlock(&lock); break; } usleep(100000); } return NULL; } int main() { pthread_mutex_init(&lock,NULL); pthread_t tid1,tid2,tid3; pthread_create(&tid1,NULL,Routine,(void*)"thread 1"); pthread_create(&tid2,NULL,Routine,(void*)"thread 2"); pthread_create(&tid3,NULL,Routine,(void*)"thread 3"); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); pthread_mutex_destroy(&lock); return 0; }

      效果:

      4、互斥量原理

      概念:

      對于互斥鎖來說被多個(gè)線程同時(shí)可見,也就是說互斥鎖本身就是一個(gè)臨界資源,所以互斥鎖想要保護(hù)臨界區(qū)的互斥性,那么互斥鎖操作則一定是原子的

      為了實(shí)現(xiàn)互斥鎖操作,大多數(shù)體系結(jié)構(gòu)都提供了swap或exchange指令,該指令的作用是把寄存器和內(nèi)存單元的數(shù)據(jù)相交換,由于只有一條指令,保證了原子性

      即使是多處理器平臺,訪問內(nèi)存的總線周期也有先后,一個(gè)處理器上的交換指令執(zhí)行時(shí)另一個(gè)處理器的交換指令只能等待總線周期

      示圖:偽代碼

      注:在交換和賦值的過程中本質(zhì)就是讓競爭的多線程中保證中有一個(gè)線程的交換得到的寄存器數(shù)據(jù)為1,即保證同一時(shí)刻只有一個(gè)競爭的線程為1,由此才能往下執(zhí)行,否則只能進(jìn)行等待

      二、可重入/線程安全

      1、基本概念

      線程安全:

      多個(gè)線程并發(fā)同一段代碼時(shí),不會出現(xiàn)不同的結(jié)果,沒有數(shù)據(jù)錯(cuò)亂的情況

      常見對全局變量或者靜態(tài)變量進(jìn)行操作,并且沒有鎖保護(hù)的情況下,會出現(xiàn)該問題

      重入:

      同一個(gè)函數(shù)被不同的執(zhí)行流調(diào)用,當(dāng)前一個(gè)流程還沒有執(zhí)行完,就有其他的執(zhí)行流再次進(jìn)入,我們稱之為重入

      一個(gè)函數(shù)在重入的情況下,運(yùn)行結(jié)果不會出現(xiàn)任何不同或者任何問題,則該函數(shù)被稱為可重入函數(shù),否則是不可重入函數(shù)

      注意:

      對于可重入來說是函數(shù)的特性,對于線程安全來說是線程的特性

      如果一個(gè)函數(shù)是可重入的,那么執(zhí)行還函數(shù)的多線程是線程安全的

      2、線程安全

      常見線程不安全的情況:

      不保護(hù)共享變量的函數(shù)

      函數(shù)狀態(tài)隨著被調(diào)用,狀態(tài)發(fā)生變化的函數(shù)

      返回指向靜態(tài)變量指針的函數(shù)

      調(diào)用線程不安全函數(shù)的函數(shù)

      常見的線程安全的情況:

      每個(gè)線程對全局變量或者靜態(tài)變量只有讀取的權(quán)限,而沒有寫入的權(quán)限,一般來說這些線程是安全的

      類或者接口對于線程來說都是原子操作

      多個(gè)線程之間的切換不會導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性

      3、重入函數(shù)

      常見不可重入的情況:

      調(diào)用了malloc/free函數(shù),因?yàn)閙alloc函數(shù)是用全局鏈表來管理堆的

      調(diào)用了標(biāo)準(zhǔn)I/O庫函數(shù),標(biāo)準(zhǔn)I/O庫的很多實(shí)現(xiàn)都以不可重入的方式使用全局?jǐn)?shù)據(jù)結(jié)構(gòu)

      可重入函數(shù)體內(nèi)使用了靜態(tài)的數(shù)據(jù)結(jié)構(gòu)

      常見可重入的情況:

      不使用全局變量或靜態(tài)變量

      不使用用malloc或者new開辟出的空間

      不調(diào)用不可重入函數(shù)

      不返回靜態(tài)或全局?jǐn)?shù)據(jù),所有數(shù)據(jù)都有函數(shù)的調(diào)用者提供

      使用本地?cái)?shù)據(jù),或者通過制作全局?jǐn)?shù)據(jù)的本地拷貝來保護(hù)全局?jǐn)?shù)據(jù)

      4、聯(lián)系與區(qū)別

      可重入與線程安全聯(lián)系:

      函數(shù)是可重入的,那就是線程安全的

      函數(shù)是不可重入的,那就不能由多個(gè)線程使用,有可能引發(fā)線程安全問題

      如果一個(gè)函數(shù)中有全局變量,那么這個(gè)函數(shù)既不是線程安全也不是可重入的

      可重入與線程安全區(qū)別:

      可重入函數(shù)是線程安全函數(shù)的一種

      線程安全不一定是可重入的,而可重入函數(shù)則一定是線程安全的

      如果將對臨界資源的訪問加上鎖,則這個(gè)函數(shù)是線程安全的,但如果這個(gè)重入函數(shù)若鎖還未釋放則會產(chǎn)生死鎖,因此是不可重入的

      三、常見鎖概念

      死鎖:

      死鎖是指在一組進(jìn)程中的各個(gè)進(jìn)程均占有不會釋放的資源,但因互相申請被其他進(jìn)程所站用不會釋放的資源而處于的一種永久等待狀態(tài)

      死鎖四個(gè)必要條件:

      互斥條件:一個(gè)資源每次只能被一個(gè)執(zhí)行流使用

      請求與保持條件:一個(gè)執(zhí)行流因請求資源而阻塞時(shí),對已獲得的資源保持不放

      不剝奪條件:一個(gè)執(zhí)行流已獲得的資源,在末使用完之前,不能強(qiáng)行剝奪

      循環(huán)等待條件:若干執(zhí)行流之間形成一種頭尾相接的循環(huán)等待資源的關(guān)系

      注:對于死鎖,四個(gè)條件缺一不可

      避免死鎖:

      破壞死鎖的四個(gè)必要條件

      加鎖順序一致

      避免鎖未釋放的場景

      資源一次性分配

      避免死鎖算法:

      死鎖檢測算法

      銀行家算法

      四、Linux線程同步

      1、基本概念

      同步概念與競態(tài)條件:

      同步:在保證數(shù)據(jù)安全的前提下,讓線程能夠按照某種特定的順序訪問臨界資源,從而有效避免饑餓問題,叫做同步

      競態(tài)條件:因?yàn)闀r(shí)序問題,而導(dǎo)致程序異常,我們稱之為競態(tài)條件

      注意:

      在多線程中,為了保護(hù)臨界資源,我們需要用到互斥鎖,但是在線程競爭的情況下,此外我們還需要考慮資源的一些特殊情況

      在特殊的情況下,可能存在某個(gè)線程多次的競爭獲取鎖,但是卻沒有做出實(shí)際的事情,這種頻繁的申請雖然沒有什么問題,但是不是很合理

      同時(shí)如果線程的競爭力非常強(qiáng),這就可能導(dǎo)致其他線程長時(shí)間競爭不到鎖,引起饑餓問題

      由此我們需要對于這種特殊的情況,保證線程能夠按照某種次序進(jìn)行臨界資源的訪問,由此就需要條件變量

      條件變量:

      當(dāng)一個(gè)線程互斥地訪問某個(gè)變量時(shí),它可能發(fā)現(xiàn)在其它線程改變狀態(tài)之前,它什么也做不了。例如一個(gè)線程訪問隊(duì)列時(shí),發(fā)現(xiàn)隊(duì)列為空,它只能等待,只到其它線程將一個(gè)節(jié)點(diǎn)添加到隊(duì)列中

      2、條件變量的使用

      初始化條件變量:

      靜態(tài)分配

      pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

      動(dòng)態(tài)分配

      初始化函數(shù)原型:

      int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrictattr);

      解釋:

      參數(shù):cond:要初始化的條件變量;attr:設(shè)置屬性,一般填NULL

      返回值:條件變量初始化成功返回0,失敗返回錯(cuò)誤碼

      銷毀函數(shù)原型:

      int pthread_cond_destroy(pthread_cond_t *cond)

      解釋:

      參數(shù):cond:需要銷毀的條件變量

      返回值:條件變量銷毀成功返回0,失敗返回錯(cuò)誤碼

      使用PTHREAD_COND_INITIALIZER初始化的條件變量不需要銷毀

      等待條件滿足函數(shù)原型:

      int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);

      解釋:

      功能:進(jìn)行等待直到條件符合被喚醒

      參數(shù):cond:需要等待的條件變量;mutex:當(dāng)前線程所處臨界區(qū)對應(yīng)的互斥鎖

      返回值:函數(shù)調(diào)用成功返回0,失敗返回錯(cuò)誤碼

      喚醒等待函數(shù)原型:

      int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond);

      解釋:

      區(qū)別:pthread_cond_signal函數(shù)用于喚醒等待隊(duì)列中首個(gè)線程;pthread_cond_broadcast函數(shù)用于喚醒等待隊(duì)列中的全部線程

      參數(shù):cond:喚醒在cond條件變量下等待的線程

      返回值:函數(shù)調(diào)用成功返回0,失敗返回錯(cuò)誤碼

      示例:協(xié)同調(diào)度其他線程

      #include #include #include pthread_cond_t cond; pthread_mutex_t mutex; void* Routine1(void* arg) { //被調(diào)度線程執(zhí)行 while(1) { pthread_cond_wait(&cond,&mutex); printf("%s is running...\n",(char*)arg); } } void* Routine2(void* arg) { int cnt=0; while(1) { if(cnt%3!=0) pthread_cond_signal(&cond); else pthread_cond_broadcast(&cond); cnt++; sleep(1); } } int main() { pthread_cond_init(&cond,NULL); pthread_mutex_init(&mutex,NULL); pthread_t tid1,tid2,tid3,tid4; pthread_create(&tid1,NULL,Routine1,(void*)"thread 1"); pthread_create(&tid2,NULL,Routine1,(void*)"thread 2"); pthread_create(&tid3,NULL,Routine1,(void*)"thread 3"); pthread_create(&tid4,NULL,Routine2,(void*)"thread 4"); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); pthread_join(tid4,NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; }

      效果:

      3、條件變量等待

      為什么條件變量等待函數(shù)第二個(gè)參數(shù)需要互斥鎖:

      條件等待是線程間同步的一種手段,如果只有一個(gè)線程,條件不滿足,一直等下去都不會滿足,所以必須要有一個(gè)線程通過某些操作,改變共享變量,使原先不滿足的條件變得滿足,并且友好的通知等待在條件變量上的線程

      條件不會無緣無故的突然變得滿足了,必然會牽扯到共享數(shù)據(jù)的變化,所以一定要用互斥鎖來保護(hù),沒有互斥鎖就無法安全的獲取和修改共享數(shù)據(jù)

      進(jìn)入訪問臨界資源時(shí),申請互斥鎖,當(dāng)遇到條件變量等待時(shí),傳入第二個(gè)參數(shù)互斥鎖,等待的同時(shí)會將所申請到的互斥鎖給釋放,被喚醒的時(shí)候會同時(shí)將互斥鎖給競爭上,保證數(shù)據(jù)安全

      示圖:

      注:如果不釋放互斥鎖,那么其他線程無法成功申請到鎖進(jìn)而改變數(shù)據(jù),也就沒有辦法通知等待的線程,那么申請到鎖的線程一直等待,別的線程無法獲取鎖也無法通知,也就會造成死鎖

      錯(cuò)誤偽代碼設(shè)計(jì):訪問臨界資源時(shí),先上鎖,發(fā)現(xiàn)條件不滿足,解鎖,然后等待在條件變量上

      pthread_mutex_lock(&mutex); while (condition_is_false) { pthread_mutex_unlock(&mutex); //解鎖之后,等待之前,條件可能已經(jīng)滿足,信號已經(jīng)發(fā)出,但是該信號可能被錯(cuò)過 pthread_cond_wait(&cond); pthread_mutex_lock(&mutex); } pthread_mutex_unlock(&mutex);

      注意:

      這里由于解鎖和等待不是原子操作。調(diào)用解鎖之后, pthread_cond_wait 之前,如果已經(jīng)有其他線程獲取到互斥量,并且條件滿足,發(fā)送了喚醒信號,那么 pthread_cond_wait 將錯(cuò)過這個(gè)信號,可能會導(dǎo)致線程永遠(yuǎn)阻塞在這個(gè) pthread_cond_wait ,所以解鎖和等待必須是一個(gè)原子操作

      調(diào)用pthread_cond_wait函數(shù)會去看條件量是否等于0:如果等于,就把互斥量改為1,直到cond_ wait返回,把條件量改成1,把互斥量恢復(fù)成原樣,也就是不滿足條件時(shí),在進(jìn)行等待前,把互斥鎖給解鎖,當(dāng)?shù)却奖粏拘褧r(shí)會自動(dòng)競爭到互斥鎖

      4、條件變量使用規(guī)范

      等待條件代碼

      pthread_mutex_lock(&mutex); while (條件為假){ pthread_cond_wait(cond, mutex); } //修改條件 pthread_mutex_unlock(&mutex);

      注:這里可能存在被偽喚醒的情況,當(dāng)喚醒的時(shí)候可能競爭鎖失敗,繼續(xù)等待,其他線程競爭成功執(zhí)行后并釋放鎖,此時(shí)條件判斷為假,但是該線程競爭到鎖后會繼續(xù)往下執(zhí)行,如果沒有再次進(jìn)行判斷可能造成錯(cuò)誤,使用while循環(huán)判斷保證醒來后條件一定為真才往下走

      給條件發(fā)送信號代碼

      pthread_mutex_lock(&mutex); //設(shè)置條件為真 pthread_cond_signal(cond); pthread_mutex_unlock(&mutex);

      五、POSIX信號量

      1、信號量概念及介紹

      基本概念:

      POSIX信號量和SystemV信號量作用相同,都是用于同步操作,達(dá)到無沖突的訪問共享資源目的。 但POSIX可以用于線程間同步

      信號量本質(zhì)是一個(gè)描述臨界資源中資源數(shù)目的計(jì)數(shù)器,信號量能夠更細(xì)粒度的對臨界資源進(jìn)行管理,每個(gè)執(zhí)行流在進(jìn)入臨界區(qū)之前都應(yīng)該先申請信號量,申請成功就有了訪問臨界資源的權(quán)限,當(dāng)訪問離開就進(jìn)行釋放信號量(類似一個(gè)訪問預(yù)定機(jī)制)

      一般來說我們是將臨界資源作為一個(gè)整體看待,所以需要使用互斥鎖讓同一時(shí)刻只能有一個(gè)執(zhí)行流進(jìn)行訪問臨界資源;實(shí)際對于臨界資源我們可以選擇分割為多個(gè)區(qū)域,當(dāng)多個(gè)執(zhí)行流需要訪問不同區(qū)域的臨界資源時(shí),那么我們可以讓這些執(zhí)行流同時(shí)訪問臨界資源的不同區(qū)域,此時(shí)不會出現(xiàn)數(shù)據(jù)不一致等問題

      信號量的PV操作:

      P操作:申請信號量獲得臨界資源中某塊資源的使用權(quán)限,當(dāng)申請成功時(shí)邏輯上臨界資源中可使用資源數(shù)目減一,對應(yīng)到信號量上就是讓計(jì)數(shù)器減一

      V操作:釋放信號量歸還臨界資源中某塊資源的使用權(quán)限,當(dāng)釋放成功時(shí)邏輯上臨界資源中可使用的資源數(shù)目加一,對應(yīng)到信號量上就是讓計(jì)數(shù)器加一

      注意:

      信號量本質(zhì)也是臨界資源(被多個(gè)執(zhí)行流申請),要保護(hù)臨界資源所以信號量的PV操作必須是原子操作

      當(dāng)臨界資源申請完時(shí),信號量為0,再申請時(shí)線程會在該信號量的等待隊(duì)列當(dāng)中進(jìn)行等待,直到有信號量被釋放時(shí)再被喚醒

      二元信號量:

      如果將信號量的初始值設(shè)置為1,那么此時(shí)該信號量叫做二元信號量

      信號量的初始值為1,說明信號量所描述的臨界資源只有一份,此時(shí)信號量的作用基本等價(jià)于互斥鎖

      2、信號量的使用

      初始化信號量函數(shù)原型:

      #include int sem_init(sem_t *sem, int pshared, unsigned int value);

      解釋:

      參數(shù):sem:需要初始化的信號量;pshared:0表示線程間共享,非零表示進(jìn)程間共享;value:信號量初始值

      返回值:初始化信號量成功返回0,失敗返回-1

      銷毀信號量函數(shù)原型:

      int sem_destroy(sem_t *sem);

      解釋:

      參數(shù):sem:需要銷毀的信號量

      返回值:銷毀信號量成功返回0,失敗返回-1

      等待信號量函數(shù)原型:

      int sem_wait(sem_t *sem); //P()

      解釋:

      功能:等待信號量,會將信號量的值減1

      參數(shù):sem:需要等待的信號量

      返回值:等待信號量成功返回0,信號量的值減一;等待信號量失敗返回-1,信號量的值保持不變

      發(fā)布信號量函數(shù)原型:

      int sem_post(sem_t *sem);//V()

      解釋:

      功能:發(fā)布信號量,表示資源使用完畢可以歸還資源了,將信號量值加1

      參數(shù):sem:需要發(fā)布的信號量

      返回值:發(fā)布信號量成功返回0,信號量的值加一;發(fā)布信號量失敗返回-1,信號量的值保持不變

      示例:

      #include #include #include #include #include class Sem{ public: Sem(int num) { sem_init(&_sem, 0, num); } ~Sem() { sem_destroy(&_sem); } void P() { sem_wait(&_sem); } void V() { sem_post(&_sem); } private: sem_t _sem; }; Sem sem(1); //二元信號量 int tickets = 2000; void* TicketGrabbing(void* arg) { std::string name = (char*)arg; while (true){ sem.P(); if (tickets > 0){ std::cout << name << " get a ticket, tickets left: " << --tickets << std::endl; sem.V(); usleep(1000); } else{ sem.V(); break; } } std::cout << name << " quit..." << std::endl; pthread_exit((void*)0); } int main() { pthread_t tid1, tid2, tid3, tid4; pthread_create(&tid1, nullptr, TicketGrabbing, (void*)"thread 1"); pthread_create(&tid2, nullptr, TicketGrabbing, (void*)"thread 2"); pthread_create(&tid3, nullptr, TicketGrabbing, (void*)"thread 3"); pthread_create(&tid4, nullptr, TicketGrabbing, (void*)"thread 4"); pthread_join(tid1, nullptr); pthread_join(tid2, nullptr); pthread_join(tid3, nullptr); pthread_join(tid4, nullptr); return 0; }

      效果:

      Linux 任務(wù)調(diào)度 多線程

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:excel表格框內(nèi)添加斜線的教程(excel表格添加斜線)
      下一篇:如何水印呢(水印該怎么弄)
      相關(guān)文章
      亚洲第一永久在线观看| 亚洲国产成人久久综合野外| 国产精品亚洲精品日韩电影| 国产成人精品日本亚洲11| 亚洲导航深夜福利| 亚洲国产亚洲片在线观看播放| 亚洲国产综合专区在线电影| 亚洲午夜精品一区二区| 久久久亚洲欧洲日产国码二区| 亚洲AV无码国产精品色午友在线 | 亚洲午夜精品国产电影在线观看| 老汉色老汉首页a亚洲| 久久伊人久久亚洲综合| 无码欧精品亚洲日韩一区| 亚洲国产精久久久久久久| 日本久久久久亚洲中字幕| 18gay台湾男同亚洲男同| 亚洲欧洲中文日产| 亚洲成a人片在线观看精品| 国产精品亚洲综合久久| 亚洲人成人伊人成综合网无码| 亚洲国产午夜精品理论片在线播放| 亚洲国产成人精品无码区花野真一 | 亚洲av永久无码嘿嘿嘿| 亚洲午夜电影在线观看| 中文日韩亚洲欧美制服| 亚洲AV日韩AV无码污污网站| www.91亚洲| 亚洲一区二区三区偷拍女厕| 亚洲AV午夜成人片| 亚洲综合免费视频| 国产亚洲精品影视在线| 精品久久久久久久久亚洲偷窥女厕| 一本色道久久88亚洲综合 | 亚洲中文字幕不卡无码| 亚洲成人在线电影| 亚洲国产精品免费在线观看| 亚洲一区二区三区免费视频| 亚洲AV无码国产剧情| 亚洲精品无码久久毛片| 亚洲中文字幕无码久久综合网|