Windows下条件变量的实现

条件变量是什么?

是一种同步对象。

 

条件变量有什么用?

用于复杂的、多线程的、多核的程序中,实现多个线程间同步任务。

 

条件变量与其它同步对象的区别?

与事件、互斥锁、segment等同步对象相比,条件变量最大的不同在于”条件“二字,其它同步对象的同步”条件“是固定的,如事件的被激发,互斥锁被释放,而条件变量的"条件"是完全自定义的,比如你可以实现当”张三赚了5块钱、李四在看电视、奥巴马访问马尔它“时,条件变量完成同步。所以说条件变量可用于复杂的同步任务。

 

Windows下有没有条件变量?

简单的答案是:没有,Windows API没有提供条件变量对象,这就是本文会存在的原因和要解决的问题。

复杂点的答案是:

  1. 使用Windows Vista之后的版本(Vista之后的版本提供了native的条件变量对象;
  2. 从开源库中抽取;
  3. 你可以自已实现;

 

方案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.

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值