Windows下條件變數的實現
條件變數是什麼?
是一種同步物件。
條件變數有什麼用?
用於複雜的、多執行緒的、多核的程式中,實現多個執行緒間同步任務。
條件變數與其它同步物件的區別?
與事件、互斥鎖、segment等同步物件相比,條件變數最大的不同在於”條件“二字,其它同步物件的同步”條件“是固定的,如事件的被激發,互斥鎖被釋放,而條件變數的"條件"是完全自定義的,比如你可以實現當”張三賺了5塊錢、李四在看電視、奧巴馬訪問馬爾它“時,條件變數完成同步。所以說條件變數可用於複雜的同步任務。
Windows下有沒有條件變數?
簡單的答案是:沒有,Windows API沒有提供條件變數物件,這就是本文會存在的原因和要解決的問題。
複雜點的答案是:
使用Windows Vista之後的版本(Vista之後的版本提供了native的條件變數物件;
從開源庫中抽取;
你可以自已實現;
方案1不現實,因為現階段你的客戶大多數還是使用windows xp/2003以下的版本,而且Vista賣的也並不好;
方案2可以參考ace庫,不過太多條件巨集和不相關程式碼,難以抽取使用(你不可能為了一個同步變數,而扯進整個龐大的ace庫吧);
方案3難度更大,必須要熟悉多執行緒程式設計,還要考慮很多變態的細節;
我就是採用方案3 -- 自已實現的。因為網上沒有現成的,不得已而為之!而你就不必重新造輪子,直接copy下面的程式碼到你的專案裡就可以直接使用了(只要你的專案是C++的)。
實現程式碼如下:
class my_mutex
{
public:
my_mutex (bool be_initial_owner = false)
{
mutex_ = CreateMutexA (NULL, be_initial_owner, NULL);
}
~my_mutex (void)
{
CloseHandle (mutex_);
}
public:
int acquire (void)
{
DWORD ret = WaitForSingleObject (mutex_, INFINITE);
return ret == WAIT_OBJECT_0 ? 0 : -1;
}
int release (void)
{
BOOL bret = ReleaseMutex (mutex_);
return bret ? 0 : -1;
}
HANDLE handle (void)
{
return mutex_;
}
protected:
HANDLE mutex_;
};
class my_semaphore
{
public:
my_semaphore (long init_count, long max_count = (std::numeric_limits<long>::max)())
{
assert (init_count >= 0 && max_count > 0 && init_count <= max_count);
sema_ = CreateSemaphoreA (NULL, init_count, max_count, NULL);
}
~my_semaphore (void)
{
CloseHandle (sema_);
}
public:
int post (long count = 1)
{
BOOL bret = ReleaseSemaphore (sema_, count, NULL);
return bret ? 0 : -1;
}
int wait (long timeout = -1)
{
DWORD ret = WaitForSingleObject (sema_, timeout);
return ret == WAIT_OBJECT_0 ? 0 : -1;
}
HANDLE handle (void)
{
return sema_;
}
protected:
HANDLE sema_;
};
template<typename MUTEX>
class my_condition
{
public:
my_condition (MUTEX &m)
: mutex_ (m), waiters_ (0), sema_ (0)
{}
~my_condition (void)
{}
public:
/// Returns a reference to the underlying mutex_;
MUTEX &mutex (void)
{
return mutex_;
}
/// Signal one waiting thread.
int signal (void)
{
// must hold the external mutex before enter
if ( waiters_ > 0 )
sema_.post ();
return 0;
}
/// Signal *all* waiting threads.
int broadcast (void)
{
// must hold the external mutex before enter
if ( waiters_ > 0 )
sema_.post (waiters_);
return 0;
}
int wait (unsigned long wait_time = -1)
{
// must hold the external mutex before enter
int ret = 0;
waiters_++;
ret = SignalObjectAndWait (mutex_.handle (), sema_.handle (), wait_time, FALSE);
mutex_.acquire ();
waiters_ --;
return ret == WAIT_OBJECT_0 ? 0 : -1;
}
protected:
MUTEX &mutex_;
/// Number of waiting threads.
long waiters_;
/// Queue up threads waiting for the condition to become signaled.
my_semaphore sema_;
};
使用條件變數的示例:
/// 公共部分
// my_mutx m;
// my_condition c (m);
/// 消費者
m.acquire();
while (!condition_is_satisfied())
{
c.wait(300);
}
handle_something();
m.release();
/// 生產者
produce_something();
m.acquire();
c.signal();
m.release();
以上程式碼採用模板實現,變件變數類my_condition的模板引數是與條件變數配合使用的互斥量型別,為了方便直接使用,互斥量型別我也一併提供了: my_mutex。
程式碼我已在專案中測試使用過,如果發現問題,歡迎各路高手批評指正。
2018.6.11新增:
根據MSDN文件對SignalObjectAndWait函式的描述,本文描述的程式碼存在死鎖的風險:條件滿足的訊號通知丟失。解決的方法就是在呼叫my_condition的wait方法時,一定要加上比較短的超時引數(比如300毫秒或者幾秒,根據實際情況調整),迴圈檢測以重新發現條件已經滿足了(條件不會丟失,儲存在訊號量裡)。MSDN原文如下:
Note that the "signal" and "wait" are not guaranteed to be performed as an atomic operation. Threads executing on other processors can observe the signaled state of the first object before the thread calling SignalObjectAndWait begins its wait on the second object.
---------------------
作者:leafarmy
來源:CSDN
原文:https://blog.csdn.net/leafarmy/article/details/4039548
版權宣告:本文為博主原創文章,轉載請