Condition variables —— 條件變數,是Windows Vista中新增加的一種處理線程同步問題的機制。它可以與“關鍵程式碼片段(critical section)”或“讀寫鎖(SRWLock)”相互配合使用,來實現線程的同步,特別是實作類別似“生產者-消費者”問題的時候,十分有效。
如果當前沒有“產品”可供“消費者線程”(讀者線程)使用,那麼該“消費者線程”要釋放掉對應的讀寫鎖或者關鍵程式碼片段,然後等待直到有一個新的“產品”被“生產者線程”(寫者線程)製造出來之後,方可繼續運行。
如果一個用來存放“產品”的資料結構滿了(比如數組),那麼對應“生產者線程”需要釋放有關的鎖和關鍵程式碼片段,同時要等待“消費線程者”消費完這些“產品”。
條件變數機制就是為了簡化上述“生產者-消費者”問題而設計的一種線程同步機制。當一個線程需要以原子的方式釋放加在資源上的鎖,並且需要被阻塞運行直到一個條件被滿足,你可以呼叫如下的函數:
BOOL SleepConditionVariableCS(
PCONDITION_VARIABLE pConditionVariable,
PCRITICAL_SECTION pCriticalSection,
DWORD dwMilliseconds);BOOL SleepConditionVariableSRW(
PCONDITION_VARIABLE pConditionVariable,
PSRWLOCK pSRWLock,
DWORD dwMilliseconds,
ULONG Flags);
正如函數名所預示的那樣,第一個函數針對關鍵程式碼片段,第二個函數針對讀寫鎖。
第1個參數pContidionVariable參數指向一個初始化的條件變數,該條件變數指明了調用者(一個線程)的條件變數,參數類型是CONDITION_VARIABLE的指標。
第2個參數指明了一個“關鍵程式碼片段”或“讀寫鎖”,它們是用來保護共用資源的。
第3個參數dwMilliseconds指明了你的線程想要等待多長時間,你可以傳遞INFINITE,指明需要無限期等待下去。
第二個函數的第4個參數Flags指明當條件變數滿足的時候,你需要對應的鎖做如何的要求(即返回的時候設定鎖,該鎖應該是什麼類型的):在“生產者線程”(寫者線程)中你應該傳遞0給該參數指明該鎖是一個“獨佔鎖定”,該鎖被線程獨佔;在“消費者線程”(讀者線程)中你應該傳遞CONDITION_VARIABLE_LOCKMODE_SHARED給該參數指明該鎖是一個“共用鎖定”,該鎖能以共用的方式為“消費者線程”服務。
在該參數調用的時候,第二個參數所指定的關鍵程式碼片段或讀寫鎖會被釋放,使得對應的線程可以訪問共用資源,從而去“生產”或“消費”;在該函數返回的時候,這個鎖又會被設定。如果是SRWLock,該函數返回的時候根據Flags參數設定讀寫鎖類型:獨佔鎖定或共用鎖定。關鍵程式碼片段則會被自動化佈建,因為關鍵程式碼片段總是“排他”的。
如果等待逾時,該函數返回FALSE,否則返回TRUE。
一個線程當被SleepConditionVariableCS或SleepConditionVariableSRW阻塞之後,可以被另一個線程通過呼叫WakeConditionVariable或WakeAllConditionVariable函數喚醒。
VOID WakeConditionVariable(
PCONDITION_VARIABLE ConditionVariable); //條件變數指標VOID WakeAllConditionVariable(
PCONDITION_VARIABLE ConditionVariable); //條件變數指標
當你呼叫WakeConditionVariable函數的時候,傳遞一個條件變數的指標給它,此時,在一個等待在同樣條件變數的上的線程的SleepConditionVariable函數內部,條件變數收到訊號,通知線程,該函數會返回,同時把對應的鎖設定為所需要的類型。
當你呼叫WakeAllConditionVarialbe函數的時候,一個過多個等待在相同條件變數上的線程會被被喚醒。喚醒多個線程是可以的,但是你需要在調用SleepConditionVariable*函數的時候設定參數Flags:給“生產者線程”傳遞0;給“消費者線程”傳遞CONDITION_VARIABLE_LOCKMODE_SHARED。所以,有些時候,“消費者線程”全部會被喚醒。或者這樣喚醒:生產者、消費者、生產者、消費者……如此迴圈。
本書還舉了一個例子,這裡就不多說了,我個人總結了下,運用條件變數應該遵循如下模式(自己是這麼認為的,如果有誤還大家請指出)
CONDITION_VARIABLE g_cvProduce; //生產條件變數
CONDITION_VARIABLE g_cvConsume; //消費條件變數
SRWLOCK g_srwLock; //讀寫鎖
DWORD WINAPI Consumer(PVOID pvParam) //消費者線程函數
{
AcquireSRWLockShard(&g_srwLock); //請求共用鎖定(讀鎖)
SleepConditionVariableSRW(g_cvConsume, &g_srwLock, INFINITE, CONDITION_VARIABLE_LOCKMODE_SHARED); //等待條件變數,會被生產者線程喚醒
//消費
ReleaseSRWLockShared(&g_srwLock); //釋放共用鎖定
WakeConditionVariable(&g_cvProduce); //喚醒一個生產者線程
}
DWORD WINAPI Producer(PVOID pvParam) //生產者線程函數
{
AcquireSRWLockExclusive(&g_srwLock); //要求一個獨佔鎖定(寫鎖)
//等待條件變數受信,會被消費者線程喚醒
SleepConditionVariableSRW(g_cvProduce, &g_srwLock, INFINITE, 0);
//生產
ReleaseSRWLockExclusive(&g_srwLock); //釋放獨佔鎖定
WakeAllConditionVariable(&g_cvConsume); //喚醒所有消費者線程
}