標籤:
linux下的同步與互斥
談到linux的並發,必然涉及到線程之間的同步和互斥,linux主要為我們提供了幾種實現線程間同步互斥的
機制,本文主要介紹互斥鎖,條件變數和訊號量。互斥鎖和條件變數包含在pthread線程庫中,使用時需要包含
<pthread.h>標頭檔。而使用訊號量時需要包含<semaphore.h>標頭檔。
1.互斥鎖
型別宣告:pthread_mutex_t mutex;
對互斥量的初始化:
程式在使用pthread_mutex_t之前需要先對其進行初始化,對於靜態分配的pthread_mutex_t變數來說,
只要將PTHREAD_MUTEX_INITIALIZER賦給變數就行了,語句如下:
static pthread_mutex_t mutex=PTHREAD_mutex_INITIALIZER;
而對於動態分配或沒有預設互斥屬性的互斥變數來說,要調用pthread_mutex_init函數來執行初始化工
作。函式宣告如下:
int pthread_mutex_init(pthread_mutex_t* mutex,const pthread_mutexattr_t* attr);
mutex是指向一個互斥量的指標,attr是指向一個屬性結構體的指標,為NULL時使用預設屬性。
需要注意的是pthread_mutex_init函數只能恰好執行一次,重複執行的結果是未定義的。
對互斥量的操作:
int pthread_mutex_lock(pthread_mutex_t* mutex);//對互斥量執行鎖定操作
int pthread_mutex_unlock(pthread_mutex_t* mutex);//對互斥量執行解鎖操作
對互斥量的銷毀:
int pthread_mutex_destroy(pthread_mutex_t* mutex);//
使用方法:
以下代碼用互斥量來保護一個臨界區:
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;//初始化
pthread_mutex_lock(&mutex);//獲得鎖
/*critical section code*/
pthread_mutex_unlock(&mutex);//釋放鎖
通常比較適用的方法是將編寫一組訪問臨界區的函數,將鎖定調用都放在這些函數中,這是確保對對象的拍他
性訪問的一種方法,這樣鎖定機制對調用線程就是透明的了。
2.條件變數
有時,我們可能需要讓線程在某個條件滿足之前一直等待,在未使用條件變數之前,您可能會使用忙等待,如
下列代碼:
while(x!=y);
這樣的方式,然而這種佔著茅坑不拉屎的行為是非常不被提倡的。因此,我們引入條件變數的概念來是線程在
某個條件不滿足是進入掛起狀態。
型別宣告:pthread_cond_t cond;
對條件變數的初始化:
程式在使用pthread_cond_t變數之前必須對其進行初始化。對於靜態分配的pthread_cond_t變數來說,
只要將PTHREAD_COND_INITIALIZER賦給變數就星了,語句如下:
pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
對於動態分配的或沒有預設屬性的變數來說,就要調用pthread_cond_init函數來執行初始化。函式宣告如下:
int pthread_cond_init(pthread_cond_t* cond,const pthread_cond_attr_t* attr);
cond是指向一個條件變數的指標,attr是指向一個屬性結構體的指標,為NULL時使用預設屬性。
對條件變數的操作:
int pthread_cond_wait(pthread_cond_* cond,pthread_mutex_t* mutex);//使線程阻塞於某個條件。
int pthread_cond_signal(pthread_cond_t* cond);//喚醒某個阻塞在cond上的線程。
對條件變數的銷毀:
int pthread_cond_destroy(pthread_cond_t* cond);
使用方法:
條件變數是與斷言或條件的測試一同調用的,以此來實現將條件變數關聯到某個斷言上去。通常線程會對
一個條件進行測試,如果失敗,就會調用pthread_cond_wait函數將線程阻塞。範例程式碼如下:
pthread_mutex_lock(&mutex);
while( a<b )
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
調用線程應該在它測試或調用pthread_cond_wait之前獲得一個互斥量,以避免在測試條件的時候有其他
線程改寫條件中變數的值。pthread_cond_wait函數中第二個參數是一個互斥量類型的指標,線程在調用
pthread_cond_wait後會隱式的原子地釋放mutex互斥量並阻塞,允許其他線程獲得互斥量並修改斷言中
的變數。當線程成功的從pthread_cond_wait中返回時,它就再次擁有了互斥量,並且不用顯式的重新獲
得互斥量。
當其他線程修改了斷言中變數的值後可以調用pthread_cond_signal函數來喚醒一個等待在某個斷言成真
的線程。也可以使用pthread_cond_broadcast(pthread_cond_t*)函數來喚醒所有等待在某個條件變數
上的線程。在修改斷言中出現的任一變數之前要獲得互斥量。範例程式碼如下:
pthread_mutex_lock(&mutex);
a++;
pthread_cond_signal(&cond);//pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
3.訊號量
訊號量是一個整型變數,它帶有兩個原子操作wait和signal。wait操作還可以被稱為down、P操作。signal操
作還可以被稱為up、V、post操作。
如果S大於0,wait操作就在一個原子操作中對其進行減量運算。如果S等於0,wait操作就就在一個原子操作中
測試S,阻塞調用程式,將調用程式放入wait的等待隊列中。
如果有線程在訊號量上阻塞,則S必然等於0,signal操作就會解除對某一個等待線程的阻塞。如果S大於0,即
沒有線程阻塞在訊號量上,signal就對S進行增量操作。
可以把訊號量理解為臨界區中資源的可用數量,wait表示對資源的申請,當沒有可用資源時訊號量為0,signal
表示線程使用資源後,對資源的釋放。
下面介紹幾種通過訊號量來控制線程按某種順序執行的方法:
1.線程1中a先於線程2中b執行[S初始話為0]
Process 1:
a;
signal(&S);
Process 2:
wait(&S);
b;
2.線程1中a於線程2中b語句交替執行[S,Q初始化為1]
Process 1:
while(1){
wait(&S);
a;
signal(&Q);
}
Process 2:
while(1){
wait(&Q);
b;
signal(&S);
}
可通過讓S為0Q為1,或讓S為1Q為0,來保證讓a或b先執行。
需要注意的是,為了避免申請多個資源發生死結,應按照相同的順序申請資源,這裡公司面試的時候經常會被
問到。
linux中訊號量有無名訊號量和命名訊號量之分,無名訊號量可用於線程之間的同步和互斥,命名訊號量可用
於進程間的通訊,命名訊號量與具名管道相似,以檔案的形式儲存於磁碟上。
無名訊號量:
型別宣告:
sem_t sem;
初始化:
int sem_init(sem_t* sem,int pshared,int value);
參數pshared為0,表示訊號量只能由初始化這個訊號量的進程中的線程使用。
value表示要將sem初始化的值。value不能為負。
操作:
int sem_post(sem_t* sem);//signal操作
int sem_wait(sem_t* sem);//wait操作
銷毀:
int sem_destroy(sem_t* sem);
使用方法:
通常先由主線程調用sem_init對訊號量進行初始化。然後在其他線程中調用post或wait函數。範例程式碼
linux下的同步與互斥