唯快不破:【多執行緒】使用訊號量進行同步

昭君出塞發表於2018-01-02
訊號量是最早出現的用來解決程式同步與互斥問題的機制(也可實現程式通訊),包括一個稱為訊號量的變數及對它進行的兩個原語操作。訊號量為一個整數,我們設這個訊號量為:sem。很顯然,我們規定在sem大於等於零的時候代表可供併發程式使用的資源實體數,sem小於零的時候,表示正在等待使用臨界區的程式的個數。根據這個原則,在給訊號量附初值的時候,我們顯然就要設初值大於零。

p操作和v操作是不可中斷的程式段,稱為原語。P,V原語中P是荷蘭語的Passeren,相當於英文的pass, V是荷蘭語的Verhoog,相當於英文中的incremnet。

且在P,V願語執行期間不允許有中斷的發生。

首先應弄清PV操作的含義:PV操作由P操作原語和V操作原語組成(原語是不可中斷的過程),對訊號量進行操作,具體定義如下:

P(S):①將訊號量S的值減1,即S=S-1;②如果S>=0,則該程式繼續執行;否則該程式置為等待狀態,排入等待佇列。

V(S):①將訊號量S的值加1,即S=S+1;②如果S>0,則該程式繼續執行;否則釋放佇列中第一個等待訊號量的程式。

PV操作的意義:我們用訊號量及PV操作來實現程式的同步和互斥。PV操作屬於程式的低階通訊。

什麼是訊號量?訊號量(semaphore)的資料結構為一個值和一個指標,指標指向等待該 訊號量的下一個程式。訊號量的值與相應資源的使用情況有關。當它的值大於0時,表示當前可用資源的數量;當它的值小於0時,其絕對值表示等待使用該資源的程式個數。注意,訊號量的值僅能由PV操作來改變。

    一般來說,訊號量S>=0時,S表示可用資源的數量。執行一次P操作意味著請求分配一個單位資源,因此S的值減1;

當S<0時,表示已經沒有可用資源,請求者必須等待別的程式釋放該類資源,它才能執行下去。而執行一個V操作意味著釋放一個單位資源,因此S的值加1;

若S<=0,表示有某些程式正在等待該資源,因此要喚醒一個等待狀態的程式,使之執行下去。

對訊號量有4種操作(include<semaphore>):
1. 初始化(initialize),int set_init(sem_t *sem, int pshared, unsigned int value);//第二引數為0表示程式間不共享
2. 等訊號(wait),int sem_wait(sem_t *sem);//訊號量大於1時,減一併返回;小於1時執行緒阻塞。
3. 給訊號(signal)int sem_post(sem_t *sem);//訊號量加一
4. 清理(destory) int sem_destory(sem_t *sem);

使用訊號量實現生產者-消費者例子:


[cpp] view plain copy
  1. #define BUFFER_SIZE 16 // 緩衝區數量  
  2.   
  3. struct prodcons  
  4. {  
  5.     // 緩衝區相關資料結構  
  6.     int buffer[BUFFER_SIZE]; /* 實際資料存放的陣列*/  
  7.     int readpos, writepos; /* 讀寫指標*/  
  8.     sem_t empty; /* 緩衝區非空的條件變數 */  
  9.     sem_t occupied; /* 緩衝區未滿的條件變數 */  
  10.     sem_t s_put;/*輸入互斥訊號量*/  
  11.     sem_t s_take;/*取出互斥訊號量*/  
  12. };  
  13. /* 初始化緩衝區結構 */  
  14. void init(struct prodcons *b)  
  15. {  
  16.     b->readpos = 0;  
  17.     b->writepos = 0;  
  18.     sem_init(&b->empty,0,BUFFER_SIZE);  
  19.     sem_init(&b->occupied,0,0);  
  20.     sem_init(&b->s_put,0,1);//二進位制訊號量,相當於互斥鎖  
  21.     sem_init(&b->s_take,0,1);  
  22. }  
  23. /* 將產品放入緩衝區,這裡是存入一個整數*/  
  24. void put(struct prodcons *b, int data)  
  25. {  
  26.     /*檢視空位訊號量,是否可以放入產品*/  
  27.     sem_wait(&b->empty);  
  28.     /*檢視是否有其他執行緒正在放入,同一時刻只能一個執行緒放入*/  
  29.     sem_wait(&b->s_put);  
  30.     /* 寫資料,並移動指標 */  
  31.     b->buffer[b->writepos] = data;  
  32.     b->writepos++;  
  33.     if (b->writepos >= BUFFER_SIZE)  
  34.         b->writepos = 0;  
  35.     sem_post(&b->s_put);//解除互斥  
  36.     sem_post(&b->occupied);//放入完成,被佔位置訊號量加一  
  37.       
  38. }   
  39. /* 從緩衝區中取出整數*/  
  40. int get(struct prodcons *b)  
  41. {  
  42.     int data;  
  43.     /*檢視被佔訊號量,是否有產品可以拿出*/  
  44.     sem_wait(&b->occupied);  
  45.     /*檢視是否有其他執行緒正在拿出,同一時刻只能一個執行緒拿出*/  
  46.     sem_wait(&b->s_take);  
  47.     /* 讀資料,移動讀指標*/  
  48.     data = b->buffer[b->readpos];  
  49.     b->readpos++;  
  50.     if (b->readpos >= BUFFER_SIZE)  
  51.         b->readpos = 0;  
  52.     sem_post(&b->s_take);//解除互斥  
  53.     sem_post(&b->empty);//拿出完成,被空位訊號量加一  
  54.     return data;  
  55. }  
  56.   
  57. /* 測試:生產者執行緒將1 到100 的整數送入緩衝區,消費者線 
  58.    程從緩衝區中獲取整數,兩者都列印資訊*/  
  59. #define OVER ( - 1)  
  60. struct prodcons buffer;  
  61. void *producer(void *data)  
  62. {  
  63.     int n;  
  64.     for (n = 0; n < 100; n++)  
  65.     {  
  66.         printf("%d --->\n", n);  
  67.         put(&buffer, n);  
  68.     } put(&buffer, OVER);  
  69.     return NULL;  
  70. }  
  71.   
  72. void *consumer(void *data)  
  73. {  
  74.     int d;  
  75.     while (1)  
  76.     {  
  77.         d = get(&buffer);  
  78.         if (d == OVER)  
  79.             break;  
  80.         printf("--->%d \n", d);  
  81.     }  
  82.     return NULL;  
  83. }  
  84.   
  85. int main(void)  
  86. {  
  87.     pthread_t th_a, th_b;  
  88.     void *retval;  
  89.     init(&buffer);  
  90.     /* 建立生產者和消費者執行緒*/  
  91.     pthread_create(&th_a, NULL, producer, 0);  
  92.     pthread_create(&th_b, NULL, consumer, 0);  
  93.     /* 等待兩個執行緒結束*/  
  94.     pthread_join(th_a, &retval);  
  95.     pthread_join(th_b, &retval);  
  96.     return 0;  
  97. }  
  98. <span style="font-family:'KaiTi_GB2312';font-size:18px;"><strong>  
  99. </strong></span>  

互斥鎖和訊號量的區別:

互斥鎖

  互斥鎖是一種保護機制。上鎖後其他執行緒不能進入保護區域的程式碼,直到鎖被釋放。

訊號量

  訊號量是一種同步機制。訊號量的值代表可用的資源數目,當值大於0代表有可用資源,則允許繼續操作,否則執行緒阻塞,等待可用資源。

當可用資源是1時,訊號量與互斥鎖基本沒區別,都起保護作用。當資源數大於1,則當訊號量大於0時執行緒都可進行操作。如果資源大於1時使用互斥鎖,則就算資源數大於1時,也只能有一個執行緒進入操作,其餘執行緒必須阻塞。

訊號量可用於程式通訊和執行緒通訊,而互斥鎖只能用於執行緒通訊。

    但是還是覺得互斥鎖就可以認為是訊號量的特例。

    現在有種突然明白什麼是利用訊號量進行多執行緒同步的含義了。












http://blog.csdn.net/yusiguyuan/article/details/14110437


相關文章