条件变量是什么?
是一种同步对象。
条件变量有什么用?
用于复杂的、多线程的、多核的程序中,实现多个线程间同步任务。
条件变量与其它同步对象的区别?
与事件、互斥锁、segment等同步对象相比,条件变量最大的不同在于”条件“二字,其它同步对象的同步”条件“是固定的,如事件的被激发,互斥锁被释放,而条件变量的"条件"是完全自定义的,比如你可以实现当”张三赚了5块钱、李四在看电视、奥巴马访问马尔它“时,条件变量完成同步。所以说条件变量可用于复杂的同步任务。
Windows下有没有条件变量?
简单的答案是:没有,Windows API没有提供条件变量对象,这就是本文会存在的原因和要解决的问题。
复杂点的答案是:
- 使用Windows Vista之后的版本(Vista之后的版本提供了native的条件变量对象;
- 从开源库中抽取;
- 你可以自已实现;
实现代码如下:
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.