線程的同步
互斥鎖,讀寫鎖,條件變量有兩種方式創建
在 posix 互斥鎖,讀寫鎖,條件變量有兩種方式創建.靜態方式和動態方式.
POSIX定義了三個個宏:
-
- PTHREAD_MUTEX_INITIALIZER 來靜態初始化互斥鎖
- PTHREAD_COND_INITIALIZER 來靜態初始化條件變量
- PTHREAD_RWLOCK_INITIALIZER 來靜態初始化讀寫鎖
在 Linux Threads 實現中,pthread_mutex_t,pthread_mutex_t,pthread_cond_t 是一個結構,而 PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_RWLOCK_INITIALIZER 則是一個結構常量。在 pthread_XXX_destroy() 用於注銷一個互斥鎖,讀寫鎖,條件變量,銷毀一個互斥鎖,讀寫鎖,條件變量即意味着釋放它所占用的資源,且要求鎖當前處於開放狀態。由於在Linux中,互斥鎖,讀寫鎖,條件變量並不占用任何資源,因此 Linux Threads 中的 pthread_XXX_destroy() 除了檢查鎖狀態以外(鎖定狀態則返回 EBUSY)沒有其他動作。
線程和 fork
子進程通過繼承整個地址空間的副本,從而父進程哪里繼承了所有互斥量,讀寫鎖和條件狀態。如果父進程包含多個線程,子進程在 fork 返回以后,如果緊接着不是馬上調用 exec 的話,就需要清理鎖狀態。 在子進程內部只存在一個線程,它是由父進程調用 fork 返回以后,如果父進程中的線程占有鎖,子進程同樣占有這些鎖,問題是子進程同樣占有這些鎖。問題是子進程不包含這些占有鎖的線程副本,所以子進程沒有辦法知道它占有的那些鎖並且需要釋放哪些鎖。如果子進程從 fork 返回以后馬上調用某個 exec 函數,就可以避免這樣的問題。這種情況下,老的地址空間被丟棄,所以鎖的狀態無關緊要。
互斥鎖
說明
從本質上說是一把鎖,在訪問共享資源前對互斥量進行加鎖,在訪問完成后釋放互斥量上的鎖,對互斥量進行加鎖以后,任何其他試圖再次對互斥量加鎖的線程將會被阻塞直到當前線程釋放該互斥量.如果釋放互斥鎖時有多個線程阻塞,所有在該互斥鎖上的阻塞線程都會變成可運行狀態,第一個變量為運行狀態的線程可以對互斥量加鎖,其他線程將會看到互斥鎖依然被鎖住,只能回去再次等待它重新變為可用.
互斥變量用 pthread_mutex_t 數據類型來表示,在使用互斥變量以前,必須首先對它進行初始化(pthread_mutex_init),可以把它設置為常量 PTHREAD_MUTEX_INITIALIZER (只對靜態分配的互斥量),也可以通過調用 pthread_mutex_init 函數進行初始化.如果動態分配互斥量.,那么在釋放內存前要調用 pthread_mutex_destroy
對互斥量的加解鎖
- pthread_mutex_lock( .. )
- pthread_mutex_trylock( .. )
- pthread_mutex_unlock( .. )
互斥量屬性
- 初始化和銷毀互斥量的屬性
- pthread_mutex_init( .. )
- pthread_mutex_destroy( .. )
- 獲得和設置互斥量的共享屬性
- pthread_mutexattr_getpshared( .. )
- pthread_mutexattr_setpshared( .. )
- 設置和修改互斥量的類型屬性
- pthread_mutexattr_gettype( .. )
- pthread_mutexattr_settype( .. )
互斥變量的進程共享屬性和類型屬性
- 共享屬性: 相互獨立的多個進程把同一個內存區域映射到他們獨自的地址空間.就像多個線程訪問共享數據一樣,多個進程訪問共享數據通常也需要同步.如果進程共享互斥量屬性設置為 PTHREAD_PROCESS_SHARED ,從多個進程共享的內存區域中分配的互斥量就可以用於這些進程同步.
- 類型屬性:
- PTHREAD_MUTEX_NORMAL:
- 這種類型的互斥鎖不會自動檢測死鎖。
- 如果一個線程試圖對一個互斥鎖重復鎖定,將會引起這個線程的死鎖。
- 如果試圖解鎖一個由別的線程鎖定的互斥鎖會引發不可預料的結果。
- 如果一個線程試圖解鎖已經被解鎖的互斥鎖也會引發不可預料的結果。
- PTHREAD_MUTEX_ERRORCHECK:
- 這種類型的互斥鎖會自動檢測死鎖。
- 如果一個線程試圖對一個互斥鎖重復鎖定,將會返回一個錯誤代碼。
- 如果試圖解鎖一個由別的線程鎖定的互斥鎖將會返回一個錯誤代碼。
- 如果一個線程試圖解鎖已經被解鎖的互斥鎖也將會返回一個錯誤代碼。
- PTHREAD_MUTEX_RECURSIVE:
- 如果一個線程對這種類型的互斥鎖重復上鎖,不會引起死鎖。
- 一個線程對這類互斥鎖的多次重復上鎖必須由這個線程來重復相同數量的解 ,這樣才能解開這個互斥鎖,別的線程才能得到這個互斥鎖。
- 如果試圖解鎖一個由別的線程鎖定的互斥鎖將會返回一個錯誤代碼。
- 如果一個線程試圖解鎖已經被解鎖的互斥鎖也將會返回一個錯誤代碼。
- 這種類型的互斥鎖只能是進程私有的(作用域屬性為PTHREAD_PROCESS_PRIVATE)
- PTHREAD_MUTEX_DEFAULT:
- 這種類型的互斥鎖不會自動檢測死鎖。
- 如果一個線程試圖對一個互斥鎖重復鎖定,將會引起不可預料的結果。
- 如果試圖解鎖一個由別的線程鎖定的互斥鎖會引發不可預料的結果。
- 如果一個線程試圖解鎖已經被解鎖的互斥鎖也會引發不可預料的結果。
- POSIX標准規定,對於某一具體的實現,可以把這種類型的互斥鎖定義為其他類型的互斥鎖。
- PTHREAD_MUTEX_NORMAL:
互斥量類型行為
例子

#include<iostream> #include<pthread.h> #include<unistd.h> #include<cstdlib> using namespace std; pthread_mutex_t g_mut; void *th_fn(void *arg) { pthread_mutex_lock(&g_mut); cout<<"sub thread"<<endl; sleep(1); pthread_mutex_unlock(&g_mut); return (void *)10; } int main() { pthread_t ptid; void *tret; pthread_mutex_init(&g_mut,NULL); pthread_create(&ptid, NULL,th_fn ,NULL); sleep(1); pthread_mutex_lock(&g_mut); cout<<"main thread"<<endl; sleep(1); pthread_mutex_unlock(&g_mut); pthread_mutex_destroy(&g_mut); pthread_join(ptid, &tret); cout<<"thread code 2 exit id = "<<(int)tret<<endl; return 0; }
讀寫鎖
讀寫鎖的三種狀態
- 讀模式下的加鎖狀態
- 寫模式下的加鎖狀態
- 不加鎖狀態
注意
一次只有一個線程可以占有寫模式的讀寫鎖,但是多個線程可以同時占有讀模式的讀寫鎖.當讀寫鎖處於讀模式鎖住狀態時,如果另外的線程視圖以寫模式加鎖,讀寫鎖通常會阻塞隨后的讀模式請求,這樣可以避免讀模式長期占用鎖,而等待的寫鎖模式鎖一直得不到請求
初始化和銷毀讀寫鎖
- pthread_rwlock_init( .. )
- pthread_rwlock_destroy( .. ) //如果在調用 pthread_rwlock_destroy 之前就釋放了讀寫鎖占用的內存空間,呢么分配給這個鎖的資源就丟失了.
對讀寫鎖的加解鎖
- pthread_rwlock_wrlock( .. )
- pthread_rwlock_trywrlock( .. )
- pthread_rwlock_rdlock( .. )
- pthread_rwlock_tryrdlock( .. )
- ipthread_rwlock_unlock( .. )
獲得讀寫鎖屬性和釋放讀寫鎖屬性
- pthread_rwlockattr_init( .. )
- pthread_rwlockattr_destroy( .. )
讀寫鎖唯一的屬性就是進程共享屬性 設置和獲得讀寫鎖共享屬性
- pthread_rwlockattr_getpshared( .. )
- pthread_rwlockattr_setpshared( .. )
例子
讀鎖(共享鎖)

#include<iostream> #include<pthread.h> #include<unistd.h> #include<cstdlib> using namespace std; pthread_rwlock_t g_rlock; void *th_fn(void *arg) { pthread_rwlock_rdlock(&g_rlock); cout<<"sub thread"<<endl; sleep(1); cout<<"out lock(sub)"<<endl; pthread_rwlock_unlock(&g_rlock); return (void *)10; } int main() { pthread_t ptid; void *tret; pthread_rwlock_init(&g_rlock,NULL); pthread_create(&ptid, NULL,th_fn ,NULL); sleep(1); pthread_rwlock_rdlock(&g_rlock); cout<<"main thread"<<endl; sleep(1); cout<<"out lock(main)"<<endl; pthread_rwlock_unlock(&g_rlock); pthread_rwlock_destroy(&g_rlock); pthread_join(ptid, &tret); cout<<"thread code 2 exit id = "<<(int)tret<<endl; return 0; }
寫鎖

#include<iostream> #include<pthread.h> #include<unistd.h> #include<cstdlib> using namespace std; pthread_rwlock_t g_rlock; void *th_fn(void *arg) { pthread_rwlock_wrlock(&g_rlock); cout<<"sub thread"<<endl; sleep(1); cout<<"out lock(sub)"<<endl; pthread_rwlock_unlock(&g_rlock); return (void *)10; } int main() { pthread_t ptid; void *tret; pthread_rwlock_init(&g_rlock,NULL); pthread_create(&ptid, NULL,th_fn ,NULL); sleep(1); pthread_rwlock_wrlock(&g_rlock); cout<<"main thread"<<endl; sleep(1); cout<<"out lock(main)"<<endl; pthread_rwlock_unlock(&g_rlock); pthread_rwlock_destroy(&g_rlock); pthread_join(ptid, &tret); cout<<"thread code 2 exit id = "<<(int)tret<<endl; return 0; }
讀些鎖復用

#include<unistd.h> #include<cstdlib> using namespace std; pthread_rwlock_t g_rlock; void *th_fn1(void *arg) { cout<<"into th_fn1"<<endl; sleep(10); pthread_rwlock_rdlock(&g_rlock); cout<<"sub thread"<<endl; sleep(1); cout<<"out rlock(sub)"<<endl; pthread_rwlock_unlock(&g_rlock); return (void *)10; } void *th_fn2(void *arg) { cout<<"into th_fn2"<<endl; sleep(1); pthread_rwlock_wrlock(&g_rlock); cout<<"sub thread"<<endl; sleep(1); cout<<"out wlock(sub)"<<endl; pthread_rwlock_unlock(&g_rlock); return (void *)10; } int main() { pthread_t ptid1,ptid2; void *tret; pthread_rwlock_init(&g_rlock,NULL); pthread_create(&ptid1, NULL,th_fn1 ,NULL); pthread_create(&ptid2, NULL,th_fn2 ,NULL); sleep(1); pthread_rwlock_rdlock(&g_rlock); cout<<"main thread"<<endl; sleep(1); cout<<"out rlock(main)"<<endl; pthread_rwlock_unlock(&g_rlock); pthread_join(ptid1 ,&tret); cout<<"thread1 code 2 exit id = "<<(int)tret<<endl; pthread_join(ptid2 ,&tret); cout<<"thread2 code 2 exit id = "<<(int)tret<<endl; pthread_rwlock_destroy(&g_rlock); return 0; }
條件變量
說明
條件變量是線程可用的另一種同步機制.條件變量給多個線程提供一個會合場所.條件變量與互斥量一起使用時,允許線程以無競爭的方式等的特定的條件發生.條件變量本身就是互斥量保護的.線程在改變條件狀態前必須首先鎖住互斥量,其他線程在獲得互斥量之前是不會察覺到這種改變,因此必須鎖定互斥量才能計算條件.在調用 pthread_cond_wait 或 pthread_cond_timedwait 時候,調用者把鎖住的互斥量傳遞給函數.函數把調用線程放到等待條件的線程列表上,然后對互斥量進行解鎖,這兩個操作是原子操作. pthread_cond_wait 返回時,互斥量再次被鎖住.
初始化和銷毀條件變量
- pthread_cond_init( .. )
- pthread_cond_destroy( .. )
等待條件函數
- pthread_cond_wait( .. )
- pthread_cond_timedwait( .. )
發送條件信號
- pthread_cond_signal( .. )
- pthread_cond_broadcast( .. )
條件變量初始化屬性和釋放屬性
- pthread_condattr_init( .. )
- pthread_condattr_destroy( .. )
條件變量支持進程共享屬性
- pthread_condattr_getshared( .. )
- pthread_condattr_setshared( .. )
例子

#include "pthread.h" #include "unistd.h" #include <iostream> using namespace std; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; void *thread1(void *); void *thread2(void *); int i=1; int main(void) { pthread_t t_a; pthread_t t_b; pthread_create(&t_a,NULL,thread2,(void *)NULL); pthread_create(&t_b,NULL,thread1,(void *)NULL); pthread_join(t_b, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0; } void *thread1(void *junk) { for(i=1;i<=9;i++) { pthread_mutex_lock(&mutex); if(i%3==0) pthread_cond_signal(&cond); else cout<<"thread_1:\t"<<i<<endl; pthread_mutex_unlock(&mutex); sleep(1); } } void *thread2(void *junk) { while(i<9) { pthread_mutex_lock(&mutex); if(i%3!=0) pthread_cond_wait(&cond,&mutex); cout<<"thread_2:\t"<<i<<endl; pthread_mutex_unlock(&mutex); sleep(1); } }