UNIX再学习 -- 线程同步
生活随笔
收集整理的這篇文章主要介紹了
UNIX再学习 -- 线程同步
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一、為什么要線程同步
當多個控制線程共享相同的內存時,需要確保每個線程看到一致的數據視圖。如果每個線程使用的變量都是其他線程不會讀取和修改的,那么就不存在一致性問題。同樣,如果變量時只讀的,每個線程同時讀取該變量也不會有一致性問題。但是,當一個線程可以修改的變量,其他線程也可以讀取或者修改的時候,我們就需要對這些線程進行同步,確保它們在訪問變量的存儲內容時不會訪問到無效的值。 當一個線程修改變量時,其他線程在讀取這個變量時可能會看到一個不一致的值。在變量修改時間多于一個存儲器訪問周期的處理器結構中,當存儲器讀與存儲器寫這兩個周期交叉時,這種不一致就會出現。當然,這種行為是與處理器體系結構相關的,但是可移植的程序并不能對使用何種處理器體系結構做出任何假設。 上圖中描述了兩個線程讀寫相同變量的假設例子。在這個例子中,線程 A 讀取變量然后給這個變量賦予一個新的數值,但寫操作需要兩個存儲器周期。當線程 B 在這兩個存儲器寫周期中間讀取這個變量時,它就會得到不一致的值。 為了解決這個問題,線程不得不使用鎖,同一時間只允許一個線程訪問該變量。 如果線程 B 希望讀取變量,它首先要獲取鎖。同樣,當線程 A 更新變量時,也需要獲取同樣的這把鎖。這樣,線程 B 在線程 A 釋放鎖以前就不能讀取變量。 總結一下,多線程共享進程中的資源,多個線程同時訪問相同的共享資源時,需要相互協調,以避免出現數據的不一致和混亂問題而線程之間的協調和通信即線程的同步。 線程同步方式有多種,接下來我們先看互斥量。一、互斥量
線程中提供了互斥量(互斥鎖)的機制來實現線程的同步。1、什么是互斥量?
互斥鎖,是一種信號量,常用來防止兩個進程或線程在同一時刻訪問相同的共享資源。可以保證以下三點:原子性:把一個互斥量鎖定為一個原子操作,這意味著操作系統(或pthread函數庫)保證了如果一個線程鎖定了一個互斥量,沒有其他線程在同一時間可以成功鎖定這個互斥量。
唯一性:如果一個線程鎖定了一個互斥量,在它解除鎖定之前,沒有其他線程可以鎖定這個互斥量。
非繁忙等待:如果一個線程已經鎖定了一個互斥量,第二個線程又試圖去鎖定這個互斥量,則第二個線程將被掛起(不占用任何cpu資源),直到第一個線程解除對這個互斥量的鎖定為止,第二個線程則被喚醒并繼續執行,同時鎖定這個互斥量。 從以上三點,我們看出可以用互斥量來保證對變量(關鍵的代碼段)的排他性訪問。 互斥量從本質上說是一把鎖,在訪問共享資源前對互斥量進行設置(加鎖),在訪問完成后釋放(解鎖)互斥量。 對互斥量進行加鎖以后,任何其他試圖再次對互斥量加鎖的線程都會被阻塞直到當前線程釋放該互斥鎖。如果釋放互斥量時有一個以上的線程阻塞,那么所有該鎖上的阻塞線程都會變成可運行狀態,第一個變為運行的線程就可以對互斥量加鎖,其他線程就會看到互斥量依然是鎖著的,只能回去再次等待它重新變為可用。在這種方式下,每次只有一個線程可以向前執行。 互斥量是用 pthread_mutex_t 數據類型表示的。在使用互斥量以前,必須首先對它進行初始化,可以把它設置為常量 PTHREAD_MUTEX_INITIALIZER (只適用于靜態分配的互斥量),也可以通過調用 pthread_mutex_init 函數初始化。
2、互斥量使用步驟
(1)定義互斥量
pthread_mutex_t mutex(2)初始化互斥鎖
靜態初始化pthread_mutex_t mtx = PTHERAD_MUTEX_INITIALIZER
動態初始化
int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); #include <pthread.h> int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 返回值:成功返回 0;失敗返回錯誤編號 要用默認的屬性初始化互斥量,只需把 attr 設為 NULL。 初始化互斥鎖之前,必須將其所在的內存清零。
如果互斥鎖已初始化,則它會處于未鎖定狀態。互斥鎖可以位于進程之間共享的內存中或者某個進程的專用內存中。
(3)使用互斥量進行加鎖
參看:pthread_mutex_lock 函數 #include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); 返回值:若成功返回 0,;失敗返回錯誤編號 對互斥量進行加鎖,需要調用 pthread_mutex_lock。如果互斥量已經上鎖,調用線程將阻塞直到互斥量被解鎖。 如果線程不希望被阻塞,它可以使用 pthread_mutex_trylock 嘗試對互斥量進行加鎖。如果調用 pthread_mutex_trylock 時互斥量處于未鎖住狀態,那么 pthread_mutex_trylock 將鎖住互斥量,不會出現阻塞直接返回 0,否則 pthread_mutex_trylock 就會失敗,不能鎖住互斥量,返回 EBUSY。(4)使用互斥量進行解鎖
對互斥量解鎖,需要調用 pthread_mutex_unlock #include <pthread.h> int pthread_mutex_unlock(pthread_mutex_t *mutex); 返回值:若成功返回 0,;失敗返回錯誤編號(5)如果不再使用,則銷毀互斥量
#include <pthread.h> int pthread_mutex_destroy(pthread_mutex_t *mutex); 返回值:成功返回 0;失敗返回錯誤碼3、示例說明
//使用互斥量解決多線程搶占資源的問題 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h>char* buf[5]; //字符指針數組 全局變量 int pos; //用于指定上面數組的下標//1.定義互斥量 pthread_mutex_t mutex;void* task(void* p) {//3.使用互斥量進行加鎖pthread_mutex_lock(&mutex);buf[pos] = p;sleep(1);pos++;//4.使用互斥量進行解鎖pthread_mutex_unlock(&mutex); }int main(void) {//2.初始化互斥量pthread_mutex_init(&mutex,0);//1.啟動一個線程 向數組中存儲內容pthread_t tid,tid2;pthread_create(&tid,NULL,task,"zhangfei");pthread_create(&tid2,NULL,task,"guanyu");//2.主線程進程等待,并且打印最終的結果pthread_join(tid,NULL);pthread_join(tid2,NULL);//5.銷毀互斥量pthread_mutex_destroy(&mutex);int i = 0;printf("字符指針數組中的內容是:");for(i = 0; i < pos; i++){printf("%s ",buf[i]);}printf("\n");return 0; }編譯:# gcc test.c -lpthread 輸出結果: 字符指針數組中的內容是:guanyu zhangfei4、示例解析
多線程搶占資源,zhangfei、guanyu 線程阻塞不能同時給數組賦值的,所以用到互斥鎖。 就跟上廁所一樣,一個一個排隊來。 注意,加鎖解鎖是需要時間的,所以互斥鎖應盡量少。三、避免死鎖
1、什么是死鎖呢?
參看:百度百科 -- 死鎖 擴展:【操作系統】處理死鎖的方法 所謂死鎖: 是指兩個或兩個以上的進程在執行過程中,由于競爭資源或者由于彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。 舉個例子: 如果線程試圖對同一個互斥量加鎖兩次,那么它自身就會陷入死鎖狀態。 //使用互斥量解決多線程搶占資源的問題 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h>char* buf[5]; //字符指針數組 全局變量 int pos; //用于指定上面數組的下標//1.定義互斥量 pthread_mutex_t mutex;void* task(void* p) {//3.使用互斥量進行加鎖pthread_mutex_lock(&mutex);pthread_mutex_lock(&mutex);buf[pos] = p;sleep(1);pos++;//4.使用互斥量進行解鎖pthread_mutex_unlock(&mutex);pthread_mutex_unlock(&mutex); }int main(void) {//2.初始化互斥量pthread_mutex_init(&mutex,0);//1.啟動一個線程 向數組中存儲內容pthread_t tid,tid2;pthread_create(&tid,NULL,task,"zhangfei");pthread_create(&tid2,NULL,task,"guanyu");//2.主線程進程等待,并且打印最終的結果pthread_join(tid,NULL);pthread_join(tid2,NULL);//5.銷毀互斥量pthread_mutex_destroy(&mutex);int i = 0;printf("字符指針數組中的內容是:");for(i = 0; i < pos; i++){printf("%s ",buf[i]);}printf("\n");return 0; } 兩個嵌套的互斥鎖會產生死鎖2、產生條件
雖然進程在運行過程中,可能發生死鎖,但死鎖的發生必須具備一定的條件,死鎖的發生必須具有以下四個必要條件。(1)互斥條件
指進程對所分配的資源進行排它性使用,即在一段時間內某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用完釋放。(2)請求和保持條件
只進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。(3)不剝奪條件
只進程已獲得的資源,在未使用之前,不能被剝奪,只能在使用完時由自己釋放。(4)環路等待條件
只在發生死鎖時,必然在一個進程 -- 資源的環形鏈,即進程集合{P0, P1, P2 ..., Pn} 中的 P0 正在等待一個 P1 占用的資源;P1 正在等待 P2 占用的資源,....,Pn正在等待已被 P0 占用的資源。3、處理方法
在系統中已經出現死鎖后,應該及時檢測到死鎖的發生,并采取適當的措施來解除死鎖。(1)預防死鎖
這是一種較簡單和直觀的事先預防的方法。方法是通過設置某些限制條件,去破壞產生死鎖的四個必要條件中的一個或者幾個,來預防發生死鎖。預防死鎖是一種較易實現的方法,已被廣泛使用。但是由于所施加的限制條件往往太嚴格,可能會導致系統資源利用率和系統吞吐量降低。(2)避免死鎖
該方法同樣是屬于事先預防的策略,但它并不須事先采取各種限制措施去破壞產生死鎖的的四個必要條件,而是在資源的動態分配過程中,用某種方法去防止系統進入不安全狀態,從而避免發生死鎖。(3)檢測和解除死鎖
先檢測:這種方法并不須事先采取任何限制性措施,也不必檢查系統是否已經進入不安全區,此方法允許系統在運行過程中發生死鎖。但可通過系統所設置的檢測機構,及時地檢測出死鎖的發生,并精確地確定與死鎖有關的進程和資源。檢測方法包括定時檢測、效率低時檢測、進程等待時檢測等。然后解除死鎖:采取適當措施,從系統中將已發生的死鎖清除掉。這是與檢測死鎖相配套的一種措施。當檢測到系統中已發生死鎖時,須將進程從死鎖狀態中解脫出來。常用的實施方法是撤銷或掛起一些進程,以便回收一些資源,再將這些資源分配給已處于阻塞狀態的進程,使之轉為就緒狀態,以繼續運行。死鎖的檢測和解除措施,有可能使系統獲得較好的資源利用率和吞吐量,但在實現上難度也最大。 參看:死鎖產生的原因及解決方法 參看:死鎖產生的原因以及解決方法
四、函數 pthread_mutex_timedlock
#include <pthread.h> #include <time.h>int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout); 返回值:成功返回 0;失敗返回錯誤編號1、函數功能
參看:pthread_mutex_timedlock 函數 當線程試圖獲取一個已加鎖的互斥量時,pthread_mutex_timedlock 互斥量原語允許綁定線程阻塞時間。pthread_mutex_timedlock 函數與 pthread_mutex_lock 是基本等價的,但是在達到超時時間時,pthread_mutex_timedlock 不會對互斥量進行加鎖,而是返回錯誤碼 ETIMEDOUT。 超時指定愿意等待的絕對時間(與相對時間對比而言,指定在時間 X 之前可以阻塞等待,而不是說愿意阻塞 Y 秒)。 這個超時時間是用 timespec 結構來表示的,它用秒和納秒來描述時間。2、示例說明
#include <stdio.h> #include <pthread.h> #include <time.h> #include "apue.h"int main (void) {int err;struct timespec tout;struct tm *tmp;char buf[64];pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_lock (&lock);printf ("mutex is locked\n");clock_gettime (CLOCK_REALTIME, &tout);tmp = localtime (&tout.tv_sec); strftime (buf, sizeof (buf), "%r", tmp);printf ("current time is %s\n", buf);tout.tv_sec += 10;err = pthread_mutex_timedlock (&lock, &tout);clock_gettime (CLOCK_REALTIME, &tout);tmp = localtime (&tout.tv_sec);strftime (buf, sizeof (buf), "%r", tmp);printf ("the time is now %s\n", buf);if (err == 0)printf ("mutex locked again\n");else printf ("can`t lock mutex again:%s\n", strerror (err));return 0; } 編譯:# gcc test.c -lpthread -lrt 輸出結果: mutex is locked current time is 03:11:30 PM the time is now 03:11:40 PM can`t lock mutex again:Connection timed out3、示例解析
時間函數可查看: 參看:百度百科 -- clock_gettime 參看:百度百科 -- localtime 參看:百度百科 -- strftime 首先,編譯時,需要加上選項 -lrt。否則,會出現錯誤:undefined reference to `clock_gettime' 再有,pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; 是靜態初始化? 這個程序故意對它已有的互斥量進行加鎖,目的是演示 pthread_mutex_timedlock 是如何工作的。不推薦在實際中使用這種策略,因為它會導致死鎖。 注意,阻塞的時間可能會有所不同,造成不同的原因有多種:開始時間可能再某秒的中間位置,系統時鐘的精度可能不足以精確到支持我們指定的超時時間值,或者在程序繼續運行前,調度延遲可能會增加時間值。
五、讀寫鎖
讀寫鎖與互斥量類似,不過讀寫鎖允許更改的并行性。互斥量要么是鎖住狀態,要么就是不加鎖狀態,而且一次只有一個線程可以對其加鎖。 讀寫鎖可以有 3 中狀態:讀模式下加鎖狀態、寫模式加鎖狀態、不加鎖狀態。 一次只有一個線程可以占有寫模式的讀寫鎖,但是多個線程可以同時占有度模式的讀寫鎖。 當讀寫鎖在寫加鎖狀態時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的線程都會被阻塞。 當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對它進行加鎖的線程都可以得到訪問權限。 但是任何希望以寫模式對此鎖進行加鎖的線程都會阻塞,直到所有的線程釋放它們的讀鎖為止。雖然各操作系統對讀寫鎖的實現各不相同,但當讀寫鎖處于讀模式鎖住的狀態,而這時有一個線程試圖以寫模式獲取鎖時,讀寫鎖通常會阻塞隨后的讀模式鎖請求。這樣可以避免讀模式鎖長期占用,而等待的寫模式鎖請求一直得不到滿足。 讀寫鎖也叫做共享互斥鎖。當讀寫鎖時讀模式鎖住時,就可以說成為以共享模式鎖住的。當它是寫模式鎖住的時候,就可以說成是互斥模式鎖住的。 與互斥量相比,讀寫鎖在使用之前必須初始化,在釋放它們底層的內存之前必須銷毀。1、讀寫鎖初始化和銷毀
POSIX 定義的讀寫鎖的數據類型是: pthread_rwlock_t。 #include <pthread.h> int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); 兩個函數返回值:若成功,返回 0;否則,返回錯誤編號 讀寫鎖通過調用 pthread_rwlock_init 進行動態初始化。如果希望讀寫鎖有默認的屬性,可以傳一個 NULL 指針給 attr。可以調用常量 PTHREAD_RWLOCK_INITIALIZER 進行靜態初始化。 在釋放讀寫鎖占用的內存之前,需要調用 phtread_rwlock_destroy 做清理工作。 如果 pthread_rwlock_init 為讀寫鎖分配了資源,pthread_rwlock_destroy 將釋放這些資源。 如果在調用 pthread_rwlock_destroy 之前就釋放了讀寫鎖占用的內存空間,那么分配給這個鎖的資源就會丟失。2、讀寫鎖解鎖
#include <pthread.h> int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); 成功則返回0, 出錯則返回錯誤編號. 要在讀模式下鎖定讀寫鎖,需要調用 pthread_rwlock_rdlock 要在寫模式下鎖定讀寫鎖,需要調用 pthread_rwlock_wrlock 不管以何種方式鎖住讀寫鎖,都可以調用 pthread_rwlcok_unlock 進行解鎖3、示例說明
參看:線程同步與互斥:讀寫鎖
#include<stdio.h>
#include<unistd.h>
#include<pthread.h> pthread_rwlock_t rwlock; //讀寫鎖
int num = 1; //讀操作,其他線程允許讀操作,卻不允許寫操作
void *fun1(void *arg)
{ while(1) { pthread_rwlock_rdlock(&rwlock); printf("read num first===%d\n",num); pthread_rwlock_unlock(&rwlock); sleep(1); }
} //讀操作,其他線程允許讀操作,卻不允許寫操作
void *fun2(void *arg)
{ while(1) { pthread_rwlock_rdlock(&rwlock); printf("read num second===%d\n",num); pthread_rwlock_unlock(&rwlock); sleep(2); }
} //寫操作,其它線程都不允許讀或寫操作
void *fun3(void *arg)
{ while(1) { pthread_rwlock_wrlock(&rwlock); num++; printf("write thread first\n"); pthread_rwlock_unlock(&rwlock); sleep(2); }
} //寫操作,其它線程都不允許讀或寫操作
void *fun4(void *arg)
{ while(1) { pthread_rwlock_wrlock(&rwlock); num++; printf("write thread second\n"); pthread_rwlock_unlock(&rwlock); sleep(1); }
} int main()
{ pthread_t ptd1, ptd2, ptd3, ptd4; pthread_rwlock_init(&rwlock, NULL);//初始化一個讀寫鎖 //創建線程 pthread_create(&ptd1, NULL, fun1, NULL); pthread_create(&ptd2, NULL, fun2, NULL); pthread_create(&ptd3, NULL, fun3, NULL); pthread_create(&ptd4, NULL, fun4, NULL); //等待線程結束,回收其資源 pthread_join(ptd1,NULL); pthread_join(ptd2,NULL); pthread_join(ptd3,NULL); pthread_join(ptd4,NULL); pthread_rwlock_destroy(&rwlock);//銷毀讀寫鎖 return 0;
}
編譯:# gcc test.c -lpthread輸出結果:
write thread second
write thread first
read num second===3
read num first===3
write thread second
read num first===4
write thread first
read num second===5
write thread second
read num first===6
..... 4、示例解析
在此示例程序中,共創建了 4 個線程,其中兩個線程用來寫入數據,兩個線程用來讀取數據。 當某個線程讀操作時,其他線程允許讀操作,卻不允許寫操作; 當某個線程寫操作時,其他線程都不允許讀或寫操作。六、條件變量
參看:多線程編程-條件變量1、條件變量概念
條件變量是線程可用的另一種同步機制。條件變量給多個線程提供了一個會合的場所。條件變量與互斥量一起使用時,允許線程以無競爭的方式等待特定的條件發生。 條件本身是由互斥量保護的。線程在改變條件狀態之前必須首先鎖住互斥量。其他線程在獲得互斥量之前不會察覺到這種改變,因為互斥量必須在鎖定以后才能計算條件。 在使用條件變量之前,必須先對它進行初始化。由 pthread_cond_t 數據類型表示的條件變量可以用兩種方式進行初始化,可以把常量 PTHREAD_COND_INITIALIZER 賦給靜態分配的條件變量,但是如果條件變量是動態分配的,則需要使用 pthread_cond_init 函數對它進行初始化。 在釋放條件變量底層的內存空間之前,可以使用 pthread_cond_destroy 函數對條件變量進行反初始化。2、條件變量初始化和銷毀
#include <pthread.h>int pthread_cond_destroy(pthread_cond_t *cond); int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 兩個函數的返回值:若成功,返回 0;否則,返回錯誤編號(1)參數解析
當參數 attr 為空指針時,函數創建的是一個缺省的條件變量。否則條件變量的屬性將由 attr 中的屬性值來決定。調用 pthread_cond_init 函數時,參數 attr 為空指針等價于 attr 中的屬性為缺省屬性,只是前者不需要 attr 所占用的內存開銷。這個函數返回時,條件變量被存放在參數 cond 指向的內存中。可以用宏 PTHREAD_COND_INITIALIZER 來初始化靜態定義的條件變量,使其具有缺省屬性。這和用pthread_cond_init 函數動態分配的效果是一樣的。初始化時不進行錯誤檢查。 如:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
3、條件變量等待
#include <pthread.h>int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); 兩個函數的返回值:若陳宮,返回 0;否則,返回錯誤編號(1)參數解析
函數將解鎖 mutex 參數指向的互斥鎖,并使當前線程阻塞在 cond 參數指向的條件變量上。被阻塞的線程可以被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() 函數是退出點,如果在調用這個函數時,已有一個掛起的退出請求,且線程允許退出,這個線程將被終止并開始執行善后處理函數,而這時和條件變量相關的互斥鎖仍將處在鎖定狀態。
? pthread_cond_timedwait 函數的功能與 pthread_cond_wait 函數相似,只是多了一個超時(tsptr)。超時值指定了我們愿意等待多長時間,它是通過 timespec 結構指定的。
4、條件變量喚醒
#include <pthread.h>int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond); 兩個函數的返回值:若成功,返回 0;否則,返回錯誤編號(1)函數解析
pthread_cond_signal 函數被用來釋放被阻塞在指定條件變量上的一個線程。至少能喚醒一個等待該條件的線程。必須在互斥鎖的保護下使用相應的條件變量。否則對條件變量的解鎖有可能發生在鎖定條件變量之前,從而造成死鎖。喚醒阻塞在條件變量上的所有線程的順序由調度策略決定,如果線程的調度策略是 SCHED_OTHER 類型的,系統將根據線程的優先級喚醒線程。如果沒有線程被阻塞在條件變量上,那么調用 pthread_cond_signal() 將沒有作用。 pthread_cond_broadcast 函數喚醒所有被 pthread_cond_wait 函數阻塞在某個條件變量上的線程,參數 cond?被用來指定這個條件變量。當沒有線程阻塞在這個條件變量上時,pthread_cond_broadcast 函數無效。由于 pthread_cond_broadcast 函數喚醒所有阻塞在某個條件變量上的線程,這些線程被喚醒后將再次競爭相應的互斥鎖,所以必須小心使用 pthread_cond_broadcast 函數。
5、示例說明
參看:線程的條件變量實例 //示例一 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> pthread_cond_t taxicond = PTHREAD_COND_INITIALIZER; pthread_mutex_t taximutex = PTHREAD_MUTEX_INITIALIZER; void *traveler_arrive(void *name) { char *p = (char *)name; printf ("Travelr: %s need a taxi now!\n", p); pthread_mutex_lock(&taximutex); pthread_cond_wait(&taxicond, &taximutex); pthread_mutex_unlock(&taximutex); printf ("traveler: %s now got a taxi!\n", p); pthread_exit(NULL); } void *taxi_arrive(void *name) { char *p = (char *)name; printf ("Taxi: %s arrives.\n", p); pthread_cond_signal(&taxicond); pthread_exit(NULL); } int main (int argc, char **argv) { char *name; pthread_t thread; pthread_attr_t threadattr; pthread_attr_init(&threadattr); name = "Jack"; pthread_create(&thread, &threadattr, taxi_arrive, name); sleep(1); name = "Susan"; pthread_create(&thread, &threadattr, traveler_arrive, name); sleep(1); name = "Mike"; pthread_create(&thread, &threadattr, taxi_arrive, name); sleep(1); return 0; } 編譯:# gcc test.c -lpthread輸出結果: Taxi: Jack arrives. Travelr: Susan need a taxi now! Taxi: Mike arrives. traveler: Susan now got a taxi! //示例二 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int travelercount = 0; pthread_cond_t taxicond = PTHREAD_COND_INITIALIZER; pthread_mutex_t taximutex = PTHREAD_MUTEX_INITIALIZER; void *traveler_arrive(void *name) { char *p = (char *)name; pthread_mutex_lock(&taximutex); printf ("traveler: %s need a taxi now!\n", p); travelercount++; pthread_cond_wait(&taxicond, &taximutex); pthread_mutex_unlock(&taximutex); printf ("traveler: %s now got a taxi!\n", p); pthread_exit(NULL); } void *taxi_arrive(void *name) { char *p = (char *)name; printf ("Taxi: %s arrives.\n", p); for(;;){ if(travelercount){ pthread_cond_signal(&taxicond); travelercount--; break; } } pthread_exit(NULL); } int main (int argc, char **argv) { char *name; pthread_t thread; pthread_attr_t threadattr; pthread_attr_init(&threadattr); name = "Jack"; pthread_create(&thread, &threadattr, taxi_arrive, name); sleep(1); name = "Susan"; pthread_create(&thread, &threadattr, traveler_arrive, name); sleep(3); name = "Mike"; pthread_create(&thread, &threadattr, taxi_arrive, name); sleep(4); return 0; } 編譯:# gcc test.c -lpthread輸出結果: Taxi: Jack arrives. traveler: Susan need a taxi now! traveler: Susan now got a taxi! Taxi: Mike arrives.七、自旋鎖
自旋鎖與互斥量功能一樣,唯一一點不同的就是互斥量阻塞后休眠讓出cpu,而自旋鎖阻塞后不會讓出cpu,會一直忙等待,直到得到鎖!!!自旋鎖在用戶態使用的比較少,在內核使用的比較多!自旋鎖的使用場景:鎖的持有時間比較短,或者說小于2次上下文切換的時間。
自旋鎖在用戶態的函數接口和互斥量一樣,把pthread_mutex_xxx()中mutex換成spin,如:pthread_spin_init()。 參看:高手進階必讀:Linux內核的同步機制
八、總結
APUE 第 11 章,線程部分,之前沒有怎么深入研究過,而且就用到了互斥量。其他的像讀寫鎖、條件變量、屏障、自旋鎖都不熟悉。 如果想繼續深入研究,參看:隨筆分類 - linux編程-線程 人家寫的不錯,而且講的不我清楚多了。總結
以上是生活随笔為你收集整理的UNIX再学习 -- 线程同步的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解simhash原理
- 下一篇: UNIX再学习 -- 线程控制