執行緒同步之訊號量同步

2021-09-30 10:51:18 字數 4337 閱讀 1893

linux中兩種基本的同步方法是訊號量和互斥量。這兩種方法很相似,而且它們可以相互通過對方來實現。

現在有個圖書館,其能容納100人,現在有兩個執行緒a、b,a執行緒執行:往圖書管理進入乙個人,b執行緒:從圖書館出來乙個人。那麼為了使得執行緒a在圖書館滿人的時候進入等待,而不是繼續往圖書館裡進人,使得b執行緒在圖書館沒人的時候等待人進入,我們可以引入訊號量:in  out  分別初始化為100和0

。那麼a則可以被表示為a:p(in) //剩餘容量減少一 如果容量為0 則等待                     b:p(out) //人數減少1  如果人數為0則阻塞等待

.........   //登記進入圖書館的人資訊                  .............  //記錄 離開圖書館的人資訊   (隨便一系列操作。)

v(out)//增加訊號量out 表示人數+1                           v(in)   //增加圖書館剩餘容量+1

通過這樣我們就實現了執行緒的同步。

訊號量作用域上鎖時

訊號量

程序間或執行緒間(linux僅執行緒間)

只要訊號量的value大於0,其他執行緒就可以sem_wait成功,成功後訊號量的value減一。若value值不大於0,則sem_wait阻塞,直到sem_post釋放後value值加一

互斥鎖執行緒間

只要被鎖住,其他任何執行緒都不可以訪問被保護的資源 

成功後否則就阻塞

下面介紹用訊號量進行同步。

訊號量概念由荷蘭科學家dijkstra首先提出。訊號量是乙個特殊型別的變數,它可以被增加或者減少。但對其的關鍵訪問被保證是原子操作,即使在乙個多執行緒程式中也是如此。

訊號量有兩種型別:

(1)二進位制訊號量。它只有0和1兩種取值。

(2)計數訊號量。它可以有更大的取值範圍。

如果要用訊號量來保護一段**,使其每次只能被乙個執行執行緒執行,就要用到二進位制訊號量、。

如果要允許有限數目的執行緒執行一段指定的**,就需要用到計數訊號量。

由於計數訊號量並不常用,而且它實際上僅僅是二進位制訊號量的一種擴充套件,這裡之介紹二進位制訊號量。

訊號量函式的名字都以sem_開頭。執行緒中使用的基本函式有4個。

注意,需要包含標頭檔案:

[cpp]view plain

copy

print?

#include

[cpp]view plain

copy

print?

intsem_init(sem_t *sem, 

intpshared, unsigned 

intvalue);  

函式解釋:

sem_init() 初始化乙個定位在 sem 的匿名信號量。value 引數指定訊號量的初始值。

pshared 引數指明訊號量是由程序內線程共享,還是由程序之間共享。如果 pshared 的值為 0,那麼訊號量將被程序內的執行緒共享,並且應該放置在所有執行緒都可見的位址上(如全域性變數,或者堆上動態分配的變數)。

如果 pshared 是非零值,那麼訊號量將在程序之間共享,並且應該定位共享記憶體區域(見 shm_open(3)、mmap(2) 和 shmget(2))。(因為通過 fork(2) 建立的孩子繼承其父親的記憶體對映,因此它也可以見到這個訊號量。)所有可以訪問共享記憶體區域的程序都可以使用sem_post(3)、sem_wait(3) 等等操作訊號量。初始化乙個已經初始的訊號量其結果未定義。

返回值

sem_init() 成功時返回 0;錯誤時,返回 -1,並把 errno 設定為合適的值。

錯誤

einval

value 超過 sem_value_max。

enosys

pshared 非零,但系統還沒有支援程序共享的訊號量。

下面是控制訊號量的兩個函式:

[cpp]view plain

copy

print?

intsem_wait(sem_t * sem);  

函式說明

sem_wait函式也是乙個原子操作,它的作用是從訊號量的值減去乙個「1」,但它永遠會先等待該訊號量為乙個非零值才開始做減法。也就是說,如果你對乙個值為2的訊號量呼叫sem_wait(),執行緒將會繼續執行,這訊號量的值將減到1。如果對乙個值為0的訊號量呼叫sem_wait(),這個函式就 會地等待直到有其它執行緒增加了這個值使它不再是0為止。如果有兩個執行緒都在sem_wait()中等待同乙個訊號量變成非零值,那麼當它被第三個執行緒增加 乙個「1」時,等待執行緒中只有乙個能夠對訊號量做減法並繼續執行,另乙個還將處於等待狀態。

返回值所有這些函式在成功時都返回 0;錯誤保持訊號量值沒有更改,-1 被返回,並設定 errno 來指明錯誤。

錯誤eintr

這個呼叫被訊號處理器中斷,

einval

sem 不是乙個有效的訊號量。

[cpp]view plain

copy

print?

intsem_post(sem_t * sem);    說明

sem_post函式的作用是給訊號量的值加上乙個「1」,它是乙個「原子操作」---即同時對同乙個訊號量做加「1」操作的兩個執行緒是不會衝突的;而同 時對同乙個檔案進行讀、加和寫操作的兩個程式就有可能會引起衝突。訊號量的值永遠會正確地加乙個「2」--因為有兩個執行緒試圖改變它。

返回值sem_post() 成功時返回 0;錯誤時,訊號量的值沒有更改,-1 被返回,並設定 errno 來指明錯誤。

錯誤einval

sem 不是乙個有效的訊號量。

eoverflow

訊號量允許的最大值將要被超過。

[cpp]view plain

copy

print?

intsem_destroy (sem_t *sem);  

這個函式也使用乙個訊號量指標做引數,歸還自己戰勝的一切資源。在清理訊號量的時候如果還有執行緒在等待它,使用者就會收到乙個錯誤。

與其它的函式一樣,這些函式在成功時都返回「0」。

下面主線程中建立了乙個新執行緒,用來統計輸入的字串中字元的個數。訊號量用來控制兩個執行緒對儲存字串陣列的訪問。

(乙個執行緒用來控制輸入,另乙個執行緒用來控制輸出)

**:[cpp]view plain

copy

print?

#include 

#include 

#include 

#include 

#include 

#include 

//執行緒函式 

void

*thread_function(

void

*arg);  

sem_t bin_sem;//訊號量物件

#define work_size 1024

char

work_area[work_size];

//工作區

intmain()  

//建立新執行緒

res = pthread_create(&a_thread,null,thread_function,null);  

if(res)  

printf("input some text.enter 'end' to finish\n"

);  

while

(strncmp(

"end"

,work_area,3) != 0)  

printf("waiting for thread to finish\n"

);  

//等待子執行緒結束,收集子執行緒資訊

res = pthread_join(a_thread,&thread_result);  

if(res)  

printf("thread joined\n"

);  

//銷毀訊號量物件

sem_destroy(&bin_sem);  

exit(exit_success);  

}  void

*thread_function(

void

*arg)  

pthread_exit(null);//執行緒終止執行

}  

執行結果:

執行緒同步之訊號量

什麼是訊號量 linux sem 訊號量是一種特殊的變數,訪問具有原子性,用於解決程序或執行緒間共享資源引發的同步問題。使用者態程序對 sem 訊號量可以有以下兩種操作 等待訊號量 當訊號量值為 0 時,程式等待 當訊號量值大於 0 時,訊號量減 1,程式繼續執行。傳送訊號量 將訊號量值加 1 通過...

Linux 執行緒同步之訊號量同步

linux中兩種基本的同步方法是訊號量和互斥量。這兩種方法很相似,而且它們可以相互通過對方來實現。下面介紹用訊號量進行同步。訊號量概念由荷蘭科學家dijkstra首先提出。訊號量是乙個特殊型別的變數,它可以被增加或者減少。但對其的關鍵訪問被保證是原子操作,即使在乙個多執行緒程式中也是如此。訊號量有兩...

Linux 執行緒同步之訊號量同步

linux中兩種基本的同步方法是訊號量和互斥量。這兩種方法很相似,而且它們可以相互通過對方來實現。下面介紹用訊號量進行同步。訊號量概念由荷蘭科學家dijkstra首先提出。訊號量是乙個特殊型別的變數,它可以被增加或者減少。但對其的關鍵訪問被保證是原子操作,即使在乙個多執行緒程式中也是如此。訊號量有兩...