ACE Condition類屬
ACE Condition類屬(條件變量)提供風格與互斥體、讀者/作者鎖和計數信號量不同的鎖定機制。當持有鎖的線程在臨界區執行代碼時,這三種機制讓協作線程進行等待。相反,條件變量通常被一個線程用於使自己等待,直到一個涉及共享數據的條件表達式到達特定的狀態。當另外的協作線程指示共享數據的狀態已發生變化,調度器就喚醒一個在該條件變量上掛起的線程。於是新喚醒的線程重新對它的條件表達式進行求值,如果共享數據已到達合適狀態,就恢復處理。
ACE線程庫提供一個叫作Condition的類來可移植地在C++包裝類中實現條件變量語義。定義方式如下:
ACE_Thread_Mutex mutex;
ACE_Condition<ACE_Thread_Mutex> cond(mutex);
該對象有兩個常用方法。
- signal()
向使用該條件變量的其它線程發送滿足條件信號。 - wait()
查詢是否滿足條件,如果滿足,則繼續往下執行;如果不滿足條件,主線程就等待在此條件變量上。條件變量隨即自動釋放互斥體,並使主線程進入睡眠。
條件變量總是與互斥體一起使用。這是一種可如下描述的一般模式:
while( expression NOT TRUE ) wait on condition variable;
條件變量不是用於互斥,往往用於線程間的協作,下面例子演示了通過條件變量實現線程協作。
#include "ace/Thread.h"
#include "ace/Synch.h"
#include <iostream>
using namespace std;
ACE_Thread_Mutex mutex;
ACE_Condition<ACE_Thread_Mutex> cond(mutex);
void* worker(void *arg)
{
ACE_OS::sleep(2); //保證eater線程的cond.wait()在worker線程的cond.signal()先執行
mutex.acquire();
ACE_OS::sleep(1);
cout<<endl<<"produce"<<endl;
cond.signal();
mutex.release();
return NULL;
}
void* eater(void *arg)
{
mutex.acquire();
cond.wait();
cout<<endl<<"eat"<<endl;
mutex.release();
return NULL;
}
int main(int argc, char *argv[])
{
ACE_Thread::spawn((ACE_THR_FUNC)worker);
ACE_OS::sleep(1);
ACE_Thread::spawn((ACE_THR_FUNC)eater);
while(true)
ACE_OS::sleep(10);
return 0;
}
這個例子中,首先創建了一個生產者線程worker和一個消費者線程eater,消費者線程執行比生產者快,兩個線程不加限制併發執行會導致先消費,後生產的情況(只是加互斥鎖也不能很好的解決,以爲無法保證生產者一定先獲得互斥體)。所以這裏通過條件變量的通知方式保證線程的順序執行:
-
消費者線程獲取互斥體,等待條件滿足(生產者生產了食品)。同時釋放互斥體,進入休眠狀態。
-
生產者獲取互斥體(雖然是消費者先獲取的互斥體,但消費者調用的wait函數會釋放消費者的互斥體),生產商品後,通過條件變量發送信號(調用signal函數)通知消費者生產完成,結束生產過程,釋放互斥體。
-
消費者收到信號後,重新獲取互斥體,完成消費過程。
使用條件變量的注意事項:
-
條件變量必須和互斥體一起使用,也就是說使用前必須加鎖(調用互斥體acquire函數),使用完後需釋放互斥體。
條件變量中的wait()和signal()成對使用的話,必須保證wait()函數在signal()之前執行,這樣才能保證wait()能收到條件滿足通知,不至於一直等待下去,形成死鎖(worker線程中的第一句話就是起的這個作用)。