【Linux C 多线程编程】互斥锁与条件变量
一、互斥鎖
?
互斥量從本質(zhì)上說就是一把鎖, 提供對共享資源的保護訪問。
1) 初始化:
在Linux下, 線程的互斥量數(shù)據(jù)類型是pthread_mutex_t. 在使用前, 要對它進行初始化:
對于靜態(tài)分配的互斥量, 可以把它設(shè)置為PTHREAD_MUTEX_INITIALIZER, 或者調(diào)用pthread_mutex_init.
對于動態(tài)分配的互斥量, 在申請內(nèi)存(malloc)之后, 通過pthread_mutex_init進行初始化, 并且在釋放內(nèi)存(free)前需要調(diào)用pthread_mutex_destroy.
原型:
int pthread_mutex_init(pthread_mutex_t *restrict?mutex, const pthread_mutexattr_t *restric attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
頭文件:#include<pthread.h>
返回值: 成功則返回0, 出錯則返回錯誤編號.
說明:?如果使用默認(rèn)的屬性初始化互斥量, 只需把attr設(shè)為NULL.?其他值在以后講解。
2) 互斥操作:
對共享資源的訪問, 要對互斥量進行加鎖, 如果互斥量已經(jīng)上了鎖, 調(diào)用線程會阻塞, 直到互斥量被解鎖. 在完成了對共享資源的訪問后, 要對互斥量進行解鎖。
首先說一下加鎖函數(shù):
頭文件:#include<pthread.h>
原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
返回值: 成功則返回0, 出錯則返回錯誤編號.
說明: 具體說一下trylock函數(shù), 這個函數(shù)是非阻塞調(diào)用模式, 也就是說, 如果互斥量沒被鎖住, trylock函數(shù)將把互斥量加鎖, 并獲得對共享資源的訪問權(quán)限; 如果互斥量被鎖住了, trylock函數(shù)將不會阻塞等待而直接返回EBUSY, 表示共享資源處于忙狀態(tài)。
再說一下解所函數(shù):
頭文件:
原型: int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值: 成功則返回0, 出錯則返回錯誤編號.
3) 死鎖:
死鎖主要發(fā)生在有多個依賴鎖存在時, 會在一個線程試圖以與另一個線程相反順序鎖住互斥量時發(fā)生. 如何避免死鎖是使用互斥量應(yīng)該格外注意的東西。
總體來講, 有幾個不成文的基本原則:
對共享資源操作前一定要獲得鎖。
完成操作以后一定要釋放鎖。
盡量短時間地占用鎖。
如果有多鎖, 如獲得順序是ABC連環(huán)扣, 釋放順序也應(yīng)該是ABC。
線程錯誤返回時應(yīng)該釋放它所獲得的鎖。
互斥鎖最簡單的使用是這樣的:
1 pthread_mutex_t mutex; //定義鎖 2 pthread_mutex_init(&mutex, NULL); //默認(rèn)屬性初始化鎖 3 pthread_mutex_lock(&mutex); //申請鎖 4 ... 5 pthread_mutex_unlock(&mutex); //釋放鎖 pthread_mutex_lock阻塞線程直到pthread_mutex_unlock被調(diào)用。
還有另外兩個函數(shù)可以用:int pthread_mutex_trylock (pthread_mutex_t *__mutex), 該函數(shù)立即返回,根據(jù)返回狀態(tài)判斷加鎖是否成功。int pthread_mutex_timedlock (pthread_mutex_t *mutex,?struct timespec *__restrict),該函數(shù)超時返回。
互斥鎖主要用來互斥訪問臨界區(qū)。用于線程的互斥。
?
設(shè)置鎖的屬性
函數(shù)pthread_mutexattr_setpshared和函數(shù)pthread_mutexattr_settype用來設(shè)置互斥鎖屬性。
前一個函數(shù)設(shè)置屬性pshared,它有兩個取值,PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用來不同進程中的線程同步,后者用于同步本進程的不同線程。在上面的例子中,我們使用的是默認(rèn)屬性PTHREAD_PROCESS_ PRIVATE。后者用來設(shè)置互斥鎖類型,可選的類型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它們分別定義了不同的上所、解鎖機制,一般情況下,選用最后一個默認(rèn)屬性。用法如下:
pthread_mutexattr_setpshared設(shè)置互斥鎖范圍
用法
1 int pthread_mutexattr_setpshared(pthread_mutexattr_t* mattr,int pshared);pthread_mutexattr_setpshared()?成功完成之后會返回零。其他任何返回值都表示出現(xiàn)了錯誤。如果出現(xiàn)以下情況,該函數(shù)將失敗并返回對應(yīng)的值。
pthread_mutexattr_getpshared(&mattr, &pshared)獲取互斥鎖范圍
1 int pthread_mutexattr_getpshared(pthread_mutexattr_t *mattr,int *pshared)此函數(shù)可為屬性對象?mattr?獲取?pshared?的當(dāng)前值。該值為?PTHREAD_PROCESS_SHARED?或?PTHREAD_PROCESS_PRIVATE。
PTHREAD_PROCESS_PRIVATE
如果互斥鎖的?pshared?屬性設(shè)置為?PTHREAD_PROCESS_PRIVATE,則僅有那些由同一個進程創(chuàng)建的線程才能夠處理該互斥鎖。
PTHREAD_PROCESS_SHARED
互斥鎖變量可以是進程專用的(進程內(nèi))變量,也可以是系統(tǒng)范圍內(nèi)的(進程間)變量。要在多個進程中的線程之間共享互斥鎖,可以在共享內(nèi)存中創(chuàng)建互斥鎖,并將?pshared?屬性設(shè)置為?PTHREAD_PROCESS_SHARED。
?
pthread_mutexattr_settype設(shè)置互斥鎖類型的屬性
用法
1 int pthread_mutexattr_settype(pthread_mutexattr_t *attr , int type);類型屬性的缺省值為?PTHREAD_MUTEX_DEFAULT
如果運行成功,pthread_mutexattr_settype?函數(shù)會返回零。否則,將返回用于指明錯誤的錯誤號。
?pthread_mutexattr_gettype獲取互斥鎖類型的屬性
用法
1 int pthread_mutexattr_gettype(pthread_mutexattr_t *attr , int *type);pthread_mutexattr_getpshared()?成功完成之后會返回零。其他任何返回值都表示出現(xiàn)了錯誤。如果出現(xiàn)以下情況,該函數(shù)將失敗并返回對應(yīng)的值。
PTHREAD_MUTEX_NORMAL
此類型的互斥鎖不會檢測死鎖。如果線程在不首先解除互斥鎖的情況下嘗試重新鎖定該互斥鎖,則會產(chǎn)生死鎖。嘗試解除由其他線程鎖定的互斥鎖會產(chǎn)生不確定的行為。如果嘗試解除鎖定的互斥鎖未鎖定,則會產(chǎn)生不確定的行為。
PTHREAD_MUTEX_ERRORCHECK
此類型的互斥鎖可提供錯誤檢查。如果線程在不首先解除鎖定互斥鎖的情況下嘗試重新鎖定該互斥鎖,則會返回錯誤。如果線程嘗試解除鎖定的互斥鎖已經(jīng)由其他線程鎖定,則會返回錯誤。如果線程嘗試解除鎖定的互斥鎖未鎖定,則會返回錯誤。
PTHREAD_MUTEX_RECURSIVE
如果線程在不首先解除鎖定互斥鎖的情況下嘗試重新鎖定該互斥鎖,則可成功鎖定該互斥鎖。 與?PTHREAD_MUTEX_NORMAL?類型的互斥鎖不同,對此類型互斥鎖進行重新鎖定時不會產(chǎn)生死鎖情況。多次鎖定互斥鎖需要進行相同次數(shù)的解除鎖定才可以釋放該鎖,然后其他線程才能獲取該互斥鎖。如果線程嘗試解除鎖定的互斥鎖已經(jīng)由其他線程鎖定,則會返回錯誤。 如果線程嘗試解除鎖定的互斥鎖未鎖定,則會返回錯誤。
PTHREAD_MUTEX_DEFAULT
如果嘗試以遞歸方式鎖定此類型的互斥鎖,則會產(chǎn)生不確定的行為。對于不是由調(diào)用線程鎖定的此類型互斥鎖,如果嘗試對它解除鎖定,則會產(chǎn)生不確定的行為。對于尚未鎖定的此類型互斥鎖,如果嘗試對它解除鎖定,也會產(chǎn)生不確定的行為。允許在實現(xiàn)中將該互斥鎖映射到其他互斥鎖類型之一。對于 Solaris 線程,PTHREAD_PROCESS_DEFAULT?會映射到?PTHREAD_PROCESS_NORMAL。
?
?pthread_mutexattr_setprotocol設(shè)置互斥鎖屬性的協(xié)議
1 int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol);如果成功完成,pthread_mutexattr_setprotocol()?會返回 0。其他任何返回值都表示出現(xiàn)了錯誤。
pthread_mutexattr_getprotocol設(shè)置互斥鎖屬性的協(xié)議
1 int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr, int *protocol);如果成功完成,pthread_mutexattr_getprotocol()?會返回 0。其他任何返回值都表示出現(xiàn)了錯誤。
-
PTHREAD_PRIO_NONE
線程的優(yōu)先級和調(diào)度不會受到互斥鎖擁有權(quán)的影響。
-
PTHREAD_PRIO_INHERIT
此協(xié)議值(如?thrd1)會影響線程的優(yōu)先級和調(diào)度。如果更高優(yōu)先級的線程因?thrd1?所擁有的一個或多個互斥鎖而被阻塞,而這些互斥鎖是用?PTHREAD_PRIO_INHERIT?初始化的,則?thrd1?將以高于它的優(yōu)先級或者所有正在等待這些互斥鎖(這些互斥鎖是?thrd1?指所擁有的互斥鎖)的線程的最高優(yōu)先級運行。
如果?thrd1?因另一個線程 (thrd3) 擁有的互斥鎖而被阻塞,則相同的優(yōu)先級繼承效應(yīng)會以遞歸方式傳播給?thrd3。
使用?PTHREAD_PRIO_INHERIT?可以避免優(yōu)先級倒置。低優(yōu)先級的線程持有較高優(yōu)先級線程所需的鎖時,便會發(fā)生優(yōu)先級倒置。只有在較低優(yōu)先級的線程釋放該鎖之后,較高優(yōu)先級的線程才能繼續(xù)使用該鎖。設(shè)置?PTHREAD_PRIO_INHERIT,以便按與預(yù)期的優(yōu)先級相反的優(yōu)先級處理每個線程。
如果為使用協(xié)議屬性值?PTHREAD_PRIO_INHERIT?初始化的互斥鎖定義了?_POSIX_THREAD_PRIO_INHERIT,則互斥鎖的屬主失敗時會執(zhí)行以下操作。屬主失敗時的行為取決于?pthread_mutexattr_setrobust_np()?的?robustness?參數(shù)的值。
-
解除鎖定互斥鎖。
-
互斥鎖的下一個屬主將獲取該互斥鎖,并返回錯誤?EOWNERDEAD。
-
互斥鎖的下一個屬主會嘗試使該互斥鎖所保護的狀態(tài)一致。如果上一個屬主失敗,則狀態(tài)可能會不一致。如果屬主成功使?fàn)顟B(tài)保持一致,則可針對該互斥鎖調(diào)用?pthread_mutex_init()?并解除鎖定該互斥鎖。
注 –如果針對以前初始化的但尚未銷毀的互斥鎖調(diào)用?pthread_mutex_init(),則該互斥鎖不會重新初始化。
-
如果屬主無法使?fàn)顟B(tài)保持一致,請勿調(diào)用?pthread_mutex_init(),而是解除鎖定該互斥鎖。在這種情況下,所有等待的線程都將被喚醒。以后對?pthread_mutex_lock()?的所有調(diào)用將無法獲取互斥鎖,并將返回錯誤代碼?ENOTRECOVERABLE?,F(xiàn)在,通過調(diào)用pthread_mutex_destroy()?來取消初始化該互斥鎖,即可使其狀態(tài)保持一致。調(diào)用?pthread_mutex_init()?可重新初始化互斥鎖。
-
如果已獲取該鎖的線程失敗并返回?EOWNERDEAD,則下一個屬主將獲取該鎖及錯誤代碼?EOWNERDEAD。
-
-
PTHREAD_PRIO_PROTECT
當(dāng)線程擁有一個或多個使用?PTHREAD_PRIO_PROTECT?初始化的互斥鎖時,此協(xié)議值會影響其他線程(如?thrd2)的優(yōu)先級和調(diào)度。thrd2?以其較高的優(yōu)先級或者以?thrd2?擁有的所有互斥鎖的最高優(yōu)先級上限運行?;诒?thrd2?擁有的任一互斥鎖阻塞的較高優(yōu)先級線程對于?thrd2?的調(diào)度沒有任何影響。
如果某個線程調(diào)用?sched_setparam()?來更改初始優(yōu)先級,則調(diào)度程序不會采用新優(yōu)先級將該線程移到調(diào)度隊列末尾。
-
線程擁有使用?PTHREAD_PRIO_INHERIT?或?PTHREAD_PRIO_PROTECT?初始化的互斥鎖
-
線程解除鎖定使用?PTHREAD_PRIO_INHERIT?或?PTHREAD_PRIO_PROTECT?初始化的互斥鎖
一個線程可以同時擁有多個混合使用?PTHREAD_PRIO_INHERIT?和?PTHREAD_PRIO_PROTECT?初始化的互斥鎖。在這種情況下,該線程將以通過其中任一協(xié)議獲取的最高優(yōu)先級執(zhí)行。
?
pthread_mutexattr_setprioceiling設(shè)置互斥鎖的優(yōu)先級上限
1 int pthread_mutexattr_setprioceiling(pthread_mutexatt_t *attr,int prioceiling, int oldceiling);attr?指示以前調(diào)用?pthread_mutexattr_init()?時創(chuàng)建的互斥鎖屬性對象。
prioceiling?指定已初始化互斥鎖的優(yōu)先級上限。優(yōu)先級上限定義執(zhí)行互斥鎖保護的臨界段時的最低優(yōu)先級。prioceiling?位于?SCHED_FIFO?所定義的優(yōu)先級的最大范圍內(nèi)。要避免優(yōu)先級倒置,請將?prioceiling?設(shè)置為高于或等于可能會鎖定特定互斥鎖的所有線程的最高優(yōu)先級。
oldceiling?包含以前的優(yōu)先級上限值。
pthread_mutexattr_getprioceiling獲取互斥鎖的優(yōu)先級上限
1 int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex,int *prioceiling);注 –
僅當(dāng)定義了?_POSIX_THREAD_PRIO_PROTECT?符號時,attr?互斥鎖屬性對象才會包括優(yōu)先級上限屬性。
?
pthread_mutexattr_setrobust_np設(shè)置互斥鎖的強健屬性
1 int pthread_mutexattr_setrobust_np(pthread_mutexattr_t *attr,int robustness);注 –
僅當(dāng)定義了符號?_POSIX_THREAD_PRIO_INHERIT?時,pthread_mutexattr_setrobust_np()?才適用。
attr?指示以前通過調(diào)用?pthread_mutexattr_init()?創(chuàng)建的互斥鎖屬性對象。
robustness?定義在互斥鎖的屬主失敗時的行為。pthread.h?中定義的?robustness?的值為?PTHREAD_MUTEX_ROBUST_NP?或PTHREAD_MUTEX_STALLED_NP。缺省值為?PTHREAD_MUTEX_STALLED_NP。
-
PTHREAD_MUTEX_ROBUST_NP
如果互斥鎖的屬主失敗,則以后對?pthread_mutex_lock()?的所有調(diào)用將以不確定的方式被阻塞。
-
PTHREAD_MUTEX_STALLED_NP
互斥鎖的屬主失敗時,將會解除鎖定該互斥鎖?;コ怄i的下一個屬主將獲取該互斥鎖,并返回錯誤?EOWNWERDEAD。
注 –應(yīng)用程序必須檢查?pthread_mutex_lock()?的返回代碼,查找返回錯誤?EOWNWERDEAD?的互斥鎖。
-
互斥鎖的新屬主應(yīng)使該互斥鎖所保護的狀態(tài)保持一致。如果上一個屬主失敗,則互斥鎖狀態(tài)可能會不一致。
-
如果新屬主能夠使?fàn)顟B(tài)保持一致,請針對該互斥鎖調(diào)用?pthread_mutex_consistent_np(),并解除鎖定該互斥鎖。
-
如果新屬主無法使?fàn)顟B(tài)保持一致,請勿針對該互斥鎖調(diào)用?pthread_mutex_consistent_np(),而是解除鎖定該互斥鎖。
所有等待的線程都將被喚醒,以后對?pthread_mutex_lock()?的所有調(diào)用都將無法獲取該互斥鎖。返回代碼為?ENOTRECOVERABLE。通過調(diào)用?pthread_mutex_destroy()?取消對互斥鎖的初始化,并調(diào)用?pthread_mutex_int()?重新初始化該互斥鎖,可使該互斥鎖保持一致。
如果已獲取該鎖的線程失敗并返回?EOWNERDEAD,則下一個屬主獲取該鎖時將返回代碼?EOWNERDEAD。
-
pthread_mutexattr_getrobust_np設(shè)置互斥鎖的強健屬性
1 int pthread_mutexattr_getrobust_np(const pthread_mutexattr_t *attr, int *robustness);注 –
僅當(dāng)定義了符號?_POSIX_THREAD_PRIO_INHERIT?時,pthread_mutexattr_getrobust_np()?才適用。
attr?指示以前通過調(diào)用?pthread_mutexattr_init()?創(chuàng)建的互斥鎖屬性對象。
robustness?是互斥鎖屬性對象的強健屬性值。
?
下面給個測試小程序進一步了解互斥,mutex互斥信號量鎖住的不是一個變量,而是阻塞住一段程序。如果對一個mutex變量testlock, 執(zhí)行了第一次pthread_mutex_lock(testlock)之后,在unlock(testlock)之前的這段時間內(nèi),如果有其他線程也執(zhí)行到了pthread_mutex_lock(testlock),這個線程就會阻塞住,直到之前的線程unlock之后才能執(zhí)行,由此,實現(xiàn)同步,也就達到保護臨界區(qū)資源的目的。
1 #include<stdio.h> 2 #include<pthread.h> 3 4 static pthread_mutex_t testlock; 5 pthread_t test_thread; 6 7 void *test() 8 { 9 pthread_mutex_lock(&testlock); 10 printf("thread Test() \n"); 11 pthread_mutex_unlock(&testlock); 12 } 13 14 int main() 15 { 16 pthread_mutex_init(&testlock, NULL); 17 pthread_mutex_lock(&testlock); 18 19 printf("Main lock \n"); 20 21 pthread_create(&test_thread, NULL, test, NULL); 22 sleep(1); //更加明顯的觀察到是否執(zhí)行了創(chuàng)建線程的互斥鎖 23 printf("Main unlock \n"); 24 pthread_mutex_unlock(&testlock); 25 26 sleep(1); 27 pthread_join(test_thread,NULL); 28 pthread_mutex_destroy(&testlock); 29 return 0; 30 } 31 32 make 33 gcc -D_REENTRANT -lpthread -o test test.c 34 35 結(jié)果: 36 Main lock 37 Main unlock 38 thread Test()?
二、條件變量
條件變量用來阻塞線程等待某個事件的發(fā)生,并且當(dāng)?shù)却氖录l(fā)生時,阻塞線程會被通知。
互斥鎖一個明顯的缺點是它只有兩種狀態(tài):鎖定和非鎖定。而條件變量通過允許線程阻塞和等待另一個線程發(fā)送信號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。
條件變量是利用線程間共享的全局變量進行同步的一種機制,主要包括兩個動作:一個線程等待"條件變量的條件成立"而掛起;另一個線程使"條件成立"(給出條件成立信號)。為了防止競爭,條件變量的使用總是和一個互斥鎖結(jié)合在一起。?
條件變量的結(jié)構(gòu)為pthread_cond_t
最簡單的使用例子:
1 pthread_cond_t cond; 2 pthread_cond_init(&cond, NULL); 3 pthread_cond_wait(&cond, &mutex); 4 ... 5 pthread_cond_signal(&cond);1)創(chuàng)建和注銷???
條件變量和互斥鎖一樣,都有靜態(tài)動態(tài)兩種創(chuàng)建方式,靜態(tài)方式使用PTHREAD_COND_INITIALIZER常量,如下:?????
pthread_cond_t?? cond=PTHREAD_COND_INITIALIZER ? ??
動態(tài)方式調(diào)用pthread_cond_init()函數(shù),API定義如下: ? ??
1 int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr)盡管POSIX標(biāo)準(zhǔn)中為條件變量定義了屬性,但在LinuxThreads中沒有實現(xiàn),因此cond_attr值通常為NULL,且被忽略。???
注銷一個條件變量需要調(diào)用pthread_cond_destroy(),只有在沒有線程在該條件變量上等待的時候才能注銷這個條件變量,否則返回EBUSY。因為Linux實現(xiàn)的條件變量沒有分配什么資源,所以注銷動作只包括檢查是否有等待線程。API定義如下:?????
??
2)等待和激發(fā)
等待:
1 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) 2 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)等待條件有兩種方式:無條件等待pthread_cond_wait()和計時等待pthread_cond_timedwait(),其中計時等待方式如果在給定時刻前條件沒有滿足,則返回ETIMEOUT,結(jié)束等待,其中abstime以與time()系統(tǒng)調(diào)用相同意義的絕對時間形式出現(xiàn),0表示格林尼治時間1970年1月1日0時0分0秒。 且進入wait時會把傳入的互斥鎖解鎖。
?無論哪種等待方式,都必須和一個互斥鎖配合,以防止多個線程同時請求pthread_cond_wait()(或pthread_cond_timedwait(),下同)的競爭條件(Race?? Condition)。
mutex互斥鎖必須是普通鎖(PTHREAD_MUTEX_TIMED_NP)或者適應(yīng)鎖(PTHREAD_MUTEX_ADAPTIVE_NP),且在調(diào)用pthread_cond_wait()前必須由本線程加鎖(pthread_mutex_lock()),而在更新條件等待隊列以前,mutex保持鎖定狀態(tài),并在線程掛起進入等待前解鎖。在條件滿足從而離開pthread_cond_wait()之前,mutex將被重新加鎖,以與進入pthread_cond_wait()前的加鎖動作對應(yīng)。???執(zhí)行pthread_cond_wait()時自動解鎖互斥量(如同執(zhí)行了 pthread_unlock_mutex),并等待條件變量觸發(fā)。這時線程掛起,不占用 CPU 時間,直到條件變量被觸發(fā)。
因此,全過程可以描述為:
(1)pthread_mutex_lock()上鎖,
(2)pthread_cond_wait()等待,等待過程分解為為:解鎖--條件滿足--加鎖
(3)pthread_mutex_unlock()解鎖。?
?
激發(fā):
1 /* 喚醒一個等待該條件變量cond的線程 */ 2 int pthread_cond_signal (pthread_cond_t *cond); 3 /* 喚醒所有等待條件變量cond的線程 */ 4 int pthread_cond_broadcast (pthread_cond_t *cond);激發(fā)條件有兩種形式,pthread_cond_signal()激活一個等待該條件的線程,存在多個等待線程時按入隊順序激活其中一個;而pthread_cond_broadcast()則激活所有等待線程。 兩者 如果沒有等待的線程,則什么也不做。
?
屬性設(shè)置:
/* 初始化條件變量屬性對象 */ int pthread_condattr_init (pthread_condattr_t * attr); /* 銷毀條件變量屬性對象 */ int pthread_condattr_destroy (pthread_condattr_t * attr); /* 獲取條件變量屬性對象在進程間共享與否的標(biāo)識 */ int pthread_condattr_getpshared (const pthread_condattr_t* attr,int* pshared); /* 設(shè)置條件變量屬性對象,標(biāo)識在進程間共享與否 */ int pthread_condattr_setpshared (pthread_condattr_t* attr, int pshared) ;PTHREAD_PROCESS_PRIVATE
如果條件變量的?pshared?屬性設(shè)置為?PTHREAD_PROCESS_PRIVATE,則僅有那些由同一個進程創(chuàng)建的線程才能夠處理該條件變量。
PTHREAD_PROCESS_SHARED
條件變量變量可以是進程專用的(進程內(nèi))變量,也可以是系統(tǒng)范圍內(nèi)的(進程間)變量。要在多個進程中的線程之間共享條件變量,可以在共享內(nèi)存中創(chuàng)建條件變量,并將?pshared?屬性設(shè)置為?PTHREAD_PROCESS_SHARED。
?
實例:
1 #include <pthread.h> 2 using namespace std; 3 4 pthread_cond_t qready = PTHREAD_COND_INITIALIZER; //初始構(gòu)造條件變量 5 pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; //初始構(gòu)造鎖 6 pthread_t tid1,tid2,tid3; 7 8 int x = 10; 9 int y = 20; 10 11 12 void *thrd_1(void *arg) 13 { 14 pthread_mutex_lock(&qlock); 15 while(x<y) 16 { 17 pthread_cond_wait(&qready,&qlock); 18 } 19 pthread_mutex_unlock(&qlock); 20 cout<<"1"<<endl; 21 sleep(5); 22 } 23 24 void *thrd_2(void *arg) 25 { 26 pthread_mutex_lock(&qlock); 27 x = 20; 28 y = 10; 29 cout<<"has change x and y"<<endl; 30 31 pthread_mutex_unlock(&qlock); 32 if(x > y) 33 { 34 pthread_cond_signal(&qready); 35 } 36 cout<<"2"<<endl; 37 } 38 39 void *thrd_3(void *arg) 40 { 41 pthread_join(tid1,NULL); 42 cout<<"3"<<endl; 43 } 44 45 int main(int argc,char **argv) 46 { 47 int err; 48 err = pthread_create(&tid1,NULL,thrd_1,NULL); 49 if(err != 0) 50 { 51 cout<<"pthread 1 create error"<<endl; 52 } 53 err = pthread_create(&tid2,NULL,thrd_2,NULL); 54 if(err != 0) 55 { 56 cout<<"pthread 2 create error"<<endl; 57 } 58 err = pthread_create(&tid3,NULL,thrd_3,NULL); 59 if(err != 0) 60 { 61 cout<<"pthread 3 create error"<<endl; 62 } 63 while(1) 64 { 65 sleep(1); 66 } 67 return 0; 68 69 }可以看到,創(chuàng)建了3個線程后,執(zhí)行順序2,1,3,即打印出的數(shù)字是213。為什么是這個順序呢?我們接下去看,當(dāng)創(chuàng)建tid1線程的時候,進入線程函數(shù),并且加上了鎖,然后進入pthread_cond_wait函數(shù),這個函數(shù)的功能是等待qready這個條件變量成功,這個條件是什么呢?我們稍后在看,現(xiàn)在我們只要知道,這個函數(shù)在qready條件沒滿足的時候會卡在這里,并且,會把傳入的互斥鎖解鎖,為什么要解鎖的,擬可以想想,如果不解鎖的話,那外部就沒有可以對x,y的修改權(quán),應(yīng)為其他兩個線程想要修改這兩個值的話都需要對qclock進行枷鎖。
好了線程1就這樣,那之后就會運行線程2,我們看線程2的線程函數(shù),該函數(shù)一開始也加了鎖,但當(dāng)線程1的pthread_cond_wait解鎖之后,他就可以繼續(xù)運行了,并且,在之后,它對x,y進行了修改,改好之后進行了解鎖,并且調(diào)用了pthread_cond_signal通知線程1,現(xiàn)在可以知道了吧。這個滿足的條件就是要x>y。 現(xiàn)在這里有個問題,一定要在發(fā)送通知之前解鎖嗎?答案是肯定的,為什么,因為如果先發(fā)送通知信號給線程1的時候,pthread_cond_wait可能在線程2的解鎖之前就返回,而當(dāng)它返回的時候,會再次將這個所進行鎖定,而這個所還沒有在線程2中解鎖,應(yīng)次會使其在次卡住。雖然這個卡住在線程2運行到解鎖處會消除,但這并不符合我們有時的需求,所以最好還是在解鎖之后在發(fā)送信號。(如果看不懂的話,可以參考下面紅色字體的部分!!!) 所以可以看出為什么線程2總是在線程1之前執(zhí)行完畢,線程3就更不用說了,pthread_join你們懂的!!! 三、信號量信號量本質(zhì)上是一個非負的整數(shù)計數(shù)器,它被用來控制對公共資源的訪問。當(dāng)公共資源增加時,調(diào)用函數(shù)sem_post()增加信號量。
只有當(dāng)信號量值大于0時,才能使用公共資源,使用后,函數(shù)sem_wait()減少信號量。函數(shù)sem_trywait()和函數(shù)
pthread_ mutex_trylock()起同樣的作用,它是函數(shù)sem_wait()的非阻塞版本。
下面我們逐個介紹和信號量有關(guān)的一些函數(shù),它們都在頭文件/usr/include/semaphore.h中定義。
信號量的數(shù)據(jù)類型為結(jié)構(gòu)sem_t,它本質(zhì)上是一個長整型的數(shù)。函數(shù)sem_init()用來初始化一個信號量。它的原型為:
extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
sem為指向信號量結(jié)構(gòu)的一個指針;pshared不為0時此信號量在進程間共享,否則只能為當(dāng)前進程的所有線程共享;value給出了信號量的初始值。
函數(shù)sem_post( sem_t *sem )用來增加信號量的值。當(dāng)有線程阻塞在這個信號量上時,調(diào)用這個函數(shù)會使其中的一個線程不在阻塞,選擇機制同樣是由線程的調(diào)度策略決定的。
函數(shù)sem_wait( sem_t *sem )被用來阻塞當(dāng)前線程直到信號量sem的值大于0,解除阻塞后將sem的值減一,表明公共資源經(jīng)使用后減少。函數(shù)sem_trywait ( sem_t *sem )是函數(shù)sem_wait()的非阻塞版本,它直接將信號量sem的值減一。
函數(shù)sem_destroy(sem_t *sem)用來釋放信號量sem。
在上面的生產(chǎn)者、消費者例子中,我們假設(shè)緩沖區(qū)最多能放10條消息。用信號量來實現(xiàn)如下:
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h>static char buff[50]; pthread_mutex_t mutex; pthread_mutex_t cond_mutex; pthread_cond_t cond; sem_t msg_cnt; //緩沖區(qū)消息數(shù) sem_t space_cnt; //緩沖區(qū)空閑數(shù)void consumeItem(char *buff) {printf("consumer item\n"); } void produceItem(char *buff) {printf("produce item\n"); } void *consumer(void *param) {while (1){sem_wait(&msg_cnt);pthread_mutex_lock(&mutex);consumeItem(buff);pthread_mutex_unlock(&mutex);sem_post(&space_cnt);}return NULL; }void *producer(void *param) {while (1){sem_wait(&space_cnt);pthread_mutex_lock(&mutex);produceItem(buff); pthread_mutex_unlock(&mutex);sem_post(&msg_cnt);}return NULL; }int main() {pthread_t tid_c, tid_p;void *retval;pthread_mutex_init(&mutex, NULL);pthread_mutex_init(&cond_mutex, NULL);pthread_cond_t cond;pthread_cond_init(&cond, NULL);sem_init(&msg_cnt, 0, 0); //初始緩沖區(qū)沒有消息sem_init(&space_cnt, 0, 10); //初始緩沖區(qū)能放10條消息pthread_create(&tid_p, NULL, producer, NULL);pthread_create(&tid_c, NULL, consumer, NULL);pthread_join(tid_p, &retval);pthread_join(tid_c, &retval);return 0; }?
總結(jié)
以上是生活随笔為你收集整理的【Linux C 多线程编程】互斥锁与条件变量的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lidgren.Network – an
- 下一篇: bzoj2442codevs4654 单