pthread條件變量深刻解析

用條件變量實現事件等待器的正確與錯誤作法 提到了8種 基於 linux pthread 條件變量實現的 Waiter classes,並分析了幾種錯誤實現的錯誤之處。本文進一步分析一下幾種正確實現的程序行爲,加深對Linux pthread 條件變量的理解。html

下面給出一個能夠用於single waiter的WaiterClass的正確實現。linux

class Waiter : private WaiterBase
{
 public:
  void wait()
  {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));
    while (!signaled_)
    {
      CHECK_SUCCESS(pthread_cond_wait(&cond_, &mutex_));
    }
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));
  }

  void signal()
  {
    CHECK_SUCCESS(pthread_mutex_lock(&mutex_));    // 0
    signaled_ = true;                              // 1
    CHECK_SUCCESS(pthread_mutex_unlock(&mutex_));  // 2
    CHECK_SUCCESS(pthread_cond_signal(&cond_));    // 3
  }

 private:
  bool signaled_ = false;
};

要實現一個正確的Waiter Class, 由 1 可知 wait() 函數的指令順序必須如上所示。但signal() 函數能夠有幾種不一樣的實現,即代碼行 1,2,3能夠有幾種不一樣的順序組合,使得Waiter Class是正確的。這幾種組合包括了1 給出的幾種正確實現。函數

首先對 代碼行1,2,3 給出全部可能的排列形式,而後一一說明其是否正確。
a. 1-2-3
b. 1-3-2
c. 2-1-3
d. 2-3-1
e. 3-1-2
f. 3-2-1測試

要分析以上6種實現的對錯,先要了解一下 pthread_cond_wait 和 pthread_cond_signal 的內部流程。線程

假設等待信號的線程爲A,發出信號的線程爲B。code

線程A,pthread_cond_wait()內部包括如下流程:htm

  1. 將 waiter 加入cond 的 _wseq 隊列 (分爲G1 G2兩個組)
  2. 釋放mutex
  3. 自旋等待,檢查 __g_signals,自旋次數結束,進入 futex_wait,休眠

線程B,pthread_cond_signal()包括如下流程:blog

  1. 檢查 cond __wseq,若沒有等待者則直接返回。
  2. 有等待者,檢查是否須要切換組(例如首次調用 wait 後 G1 爲空,G2有一個等待者,則首次調用 signal 後須要將 G2 切換爲 G1),遞增 __g_signals,遞減 __g_size(未喚醒的 waiters 個數),再調用 futex_wake。

signal 將一直喚醒G1組的 waiters 直到 G1 全部的 waiters 都被喚醒。若此過程種有新到達的waiter, 則存入G2 組(以後到達的 waiter 也存入G2)。G2組會在下次signal 調用時轉爲G1組,所以signal永遠只喚醒G1 組的 waiters。(參考)隊列

有了以上的細節,能夠很容易的得出問題的結論。事件

  1. 首先應當排除e和f。ef拿到鎖又立刻釋放鎖,1和2都不被鎖保護,1,2執行的時機能夠任意穿插到wait()函數各語句zhi'jian,從而語句2發出的信號極可能會丟失
  2. d也是錯誤的。一種致使線程A餓死的時序:2發信號,喚醒線程A,3釋放鎖,A得到鎖,判斷布爾值,條件爲真,再次進入等待,一直休眠
  3. ac是正確的。無論發信號時是否修改了布爾值,被喚醒的A線程始終沒法獲得mutex,直到3釋放鎖,futex_wake通知線程A, A才能夠拿到鎖,從pthread_cond_wait返回,繼續向下執行,此時布爾值已經改變,條件測試爲假,跳出循環,繼續執行。事實上,因爲編譯器亂序和CPU 亂序,ac有可能運行的次序是相同的。
  4. b也是正確的。B線程在修改布爾變量後釋放鎖,此時線程A依然處在休眠狀態,不知道發生的一切,只有當B發信號喚醒A時,A才能夠看到這一切,成功得到鎖,進而繼續向下執行。

總結髮現,只要布爾值的修改是在釋放鎖的操做以前,就能保證其正確性。

相關文章
相關標籤/搜索