Linux系統下的多執行緒程式設計 條件變數 訊號量

2021-06-28 07:41:10 字數 3898 閱讀 8514

條件變數

使用互斥鎖來實現執行緒間資料的共享和通訊,互斥鎖乙個明顯的缺點是它只有兩種狀態:鎖定和非鎖定。而條件變數通過允許執行緒阻塞和等待另乙個執行緒傳送訊號的方法彌補了互斥鎖的不足,它常和互斥鎖一起使用。使用時,條件變數被用來阻塞乙個執行緒,當條件不滿足時,執行緒往往解開相應的互斥鎖並等待條件發生變化。一旦其它的某個執行緒改變了條件變數,它將通知相應的條件變數喚醒乙個或多個正被此條件變數阻塞的執行緒。這些執行緒將重新鎖定互斥鎖並重新測試條件是否滿足。一般說來,條件變數被用來進行線承間的同步。

條件變數的結構為pthread_cond_t,函式pthread_cond_init()被用來初始化乙個條件變數。它的原型為:

extern int pthread_cond_init __p ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));

其中cond是乙個指向結構pthread_cond_t的指標,cond_attr是乙個指向結構pthread_condattr_t的指標。結構 pthread_condattr_t是條件變數的屬性結構,和互斥鎖一樣我們可以用它來設定條件變數是程序內可用還是程序間可用,預設值是 pthread_ process_private,即此條件變數被同一程序內的各個執行緒使用。注意初始化條件變數只有未被使用時才能重新初始化或被釋放。釋放乙個條件變數的函式為pthread_cond_ destroy(pthread_cond_t cond)。 

函式pthread_cond_wait()使執行緒阻塞在乙個條件變數上。它的函式原型為:

extern int pthread_cond_wait __p ((pthread_cond_t *__cond,pthread_mutex_t *__mutex));

執行緒解開mutex指向的鎖並被條件變數cond阻塞。執行緒可以被函式pthread_cond_signal和函式 pthread_cond_broadcast喚醒,但是要注意的是,條件變數只是起阻塞和喚醒執行緒的作用,具體的判斷條件還需使用者給出,例如乙個變數是否為0等等,這一點我們從後面的例子中可以看到。執行緒被喚醒後,它將重新檢查判斷條件是否滿足,如果還不滿足,一般說來執行緒應該仍阻塞在這裡,被等待被下一次喚醒。這個過程一般用while語句實現。

另乙個用來阻塞執行緒的函式是pthread_cond_timedwait(),它的原型為:

extern int pthread_cond_timedwait __p ((pthread_cond_t *__cond,pthread_mutex_t *__mutex, __const struct timespec *__abstime));

它比函式pthread_cond_wait()多了乙個時間引數,經歷abstime段時間後,即使條件變數不滿足,阻塞也被解除。

函式pthread_cond_signal()的原型為:

extern int pthread_cond_signal __p ((pthread_cond_t *__cond));

它用來釋放被阻塞在條件變數cond上的乙個執行緒。多個執行緒阻塞在此條件變數上時,哪乙個執行緒被喚醒是由執行緒的排程策略所決定的。要注意的是,必須用保護條件變數的互斥鎖來保護這個函式,否則條件滿足訊號又可能在測試條件和呼叫pthread_cond_wait函式之間被發出,從而造成無限制的等待。下面是使用函式pthread_cond_wait()和函式pthread_cond_signal()的乙個簡單的例子。

pthread_mutex_t count_lock;

pthread_cond_t count_nonzero;

unsigned count;

decrement_count ()

increment_count()

count值為0時,decrement函式在pthread_cond_wait處被阻塞,並開啟互斥鎖count_lock。此時,當呼叫到函式 increment_count時,pthread_cond_signal()函式改變條件變數,告知decrement_count()停止阻塞。讀者可以試著讓兩個執行緒分別執行這兩個函式,看看會出現什麼樣的結果。

函式pthread_cond_broadcast(pthread_cond_t *cond)用來喚醒所有被阻塞在條件變數cond上的執行緒。這些執行緒被喚醒後將再次競爭相應的互斥鎖,所以必須小心使用這個函式。

訊號量訊號量本質上是乙個非負的整數計數器,它被用來控制對公共資源的訪問。當公共資源增加時,呼叫函式sem_post()增加訊號量。只有當訊號量值大於0時,才能使用公共資源,使用後,函式sem_wait()減少訊號量。函式sem_trywait()和函式pthread_ mutex_trylock()起同樣的作用,它是函式sem_wait()的非阻塞版本。下面我們逐個介紹和訊號量有關的一些函式,它們都在標頭檔案 /usr/include/semaphore.h中定義。

訊號量的資料型別為結構sem_t,它本質上是乙個長整型的數。函式sem_init()用來初始化乙個訊號量。它的原型為:

extern int sem_init __p ((sem_t *__sem, int __pshared, unsigned int __value));

sem為指向訊號量結構的乙個指標;pshared不為0時此訊號量在程序間共享,否則只能為當前程序的所有執行緒共享;value給出了訊號量的初始值。

函式sem_post( sem_t *sem )用來增加訊號量的值。當有執行緒阻塞在這個訊號量上時,呼叫這個函式會使其中的乙個執行緒不在阻塞,選擇機制同樣是由執行緒的排程策略決定的。

函式sem_wait( sem_t *sem )被用來阻塞當前執行緒直到訊號量sem的值大於0,解除阻塞後將sem的值減一,表明公共資源經使用後減少。函式sem_trywait ( sem_t *sem )是函式sem_wait()的非阻塞版本,它直接將訊號量sem的值減一。

函式sem_destroy(sem_t *sem)用來釋放訊號量sem。

下面我們來看乙個使用訊號量的例子。在這個例子中,一共有4個執行緒,其中兩個執行緒負責從檔案讀取資料到公共的緩衝區,另兩個執行緒從緩衝區讀取資料作不同的處理(加和乘運算)。

/* file sem.c */

#include <stdio.h>

#include <pthread.h>

#include <semaphore.h>

#define maxstack 100

int stack[maxstack][2];

int size=0;

sem_t sem;

/* 從檔案1.dat讀取資料,每讀一次,訊號量加一*/

void readdata1(void)

fclose(fp);

}/*從檔案2.dat讀取資料*/

void readdata2(void)

fclose(fp);

}/*阻塞等待緩衝區有資料,讀取資料後,釋放空間,繼續等待*/

void handledata1(void)

}void handledata2(void)

}int main(void)

在linux下,我們用命令gcc -lpthread sem.c -o sem生成可執行檔案sem。我們事先編輯好資料檔案1.dat和2.dat,假設它們的內容分別為1 2 3 4 5 6 7 8 9 10和 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 ,我們執行sem,得到如下的結果:

multiply:-1*-2=2

plus:-1+-2=-3

multiply:9*10=90

plus:-9+-10=-19

multiply:-7*-8=56

plus:-5+-6=-11

multiply:-3*-4=12

plus:9+10=19

plus:7+8=15

plus:5+6=11

從中我們可以看出各個執行緒間的競爭關係。而數值並未按我們原先的順序顯示出來這是由於size這個數值被各個執行緒任意修改的緣故。這也往往是多執行緒程式設計要注意的問題。

Linux系統下C 多執行緒程式設計

2017 0924 c 編譯執行 2017 0924 記錄執行時間 1 c 編譯執行 linux下,c 可執行檔案 out 編譯 c g ex1.cpp o ex1 ex1.cpp 為原始檔,名字字尾名隨意,ex1為可執行性檔案,預設為.out檔案,字尾名省略 c語言 gcc ex1.c o ex1...

linux下多執行緒程式設計

先看執行的結果 1 主函式正在建立執行緒,2執行緒1被建立 3 thread1 i m thread 1th 4執行緒2被建立 5 thread2 i m thread 2nd 6 thread2 number 0 7執行緒3被建立 8主函式正在等待執行緒結束.9 thread1 number 0 ...

Linux下的多執行緒程式設計

執行緒是作業系統能夠進行排程運算的最小單位,它被包含在程序之中,是程序中的實際運作單位。一條執行緒指的是程序中乙個單一順序的控制流,乙個程序可以併發多個執行緒,每條執行緒執行不同的任務。include intpthread create pthread t pthread,const pthread...