条件变量使用互斥锁和.wait()函数解锁互斥锁,以便另一个线程可以访问共享数据。当条件变量被通知时,它尝试再次锁定互斥锁以使用共享数据。
这种模式在Anthony Williams的以下concurrent_queue示例中使用:
template<typename Data>
class concurrent_queue
{
private:
boost::condition_variable the_condition_variable;
public:
void wait_for_data()
{
boost::mutex::scoped_lock lock(the_mutex);
while(the_queue.empty())
{
the_condition_variable.wait(lock);
}
}
void push(Data const& data)
{
boost::mutex::scoped_lock lock(the_mutex);
bool const was_empty=the_queue.empty();
the_queue.push(data);
if(was_empty)
{
the_condition_variable.notify_one();
}
}
};
由于代码使用了std::queue,因此明确在访问队列时必须锁定互斥量。 但是假设使用PPL中的Microsoft Concurrency :: concurrent_queue而不是std :: queue。像empty,push和try_pop这样的成员函数是线程安全的。在这种情况下,我是否仍需要锁定互斥量,或者可以像这样使用条件变量,而不会创建任何可能的竞争条件。
我的代码(在多线程中似乎有效,但这意味着什么?)如下所示。我有一个生产者将项目推入Microsoft的concurrent_queue中,还有一个后台线程在等待此队列中的新项目。
消费者/后台线程:
while(runFlag) //atomic
{
while(the_queue.empty() && runFlag) //wait only when thread should still run
{
boost::mutex mtx; //local mutex thats locked afterwards. awkward.
boost::mutex::scoped_lock lock(mtx);
condition.wait(lock);
}
Data d;
while(!the_queue.empty() && the_queue.try_pop(d))
{
//process data
}
}
生产者/主线程:
const bool was_empty = the_queue.empty();
Data d;
the_queue.push(d);
if(was_empty) cond_var.notify_one();
关机程序:
bool expected_run_state = true;
if(runLoop.compare_exchange_strong(expected_run_state, false))
{
//atomically set our loop flag to false and
//notify all clients of the queue to wake up and exit
cond_var.notify_all();
}
如上所述,这段代码似乎可以工作,但这并不一定意味着它是正确的。特别是本地互斥锁仅在条件变量接口强制我使用互斥锁时才被使用,似乎是一个非常糟糕的想法。我想使用条件变量,因为添加到队列中的数据项之间的时间很难预测,我必须像这样定期创建睡眠和唤醒:
if(the_queue.empty()) Sleep(short_amount_of_time);
是否有其他,可能是操作系统(在我这种情况下:Windows)特定的工具,使后台线程睡眠直到满足某些条件而不时常唤醒并检查条件?
the_queue.empty()
而只是始终调用cond.notify()
,那么这是否意味着我实际上可以安全地使用实际上不锁定任何内容的本地互斥量?当然,这意味着我会尝试过于频繁地唤醒后台任务,但是当没有人等待条件变量时,通知也可能不会造成任何伤害,对吧? - user2460318