条件变量使用互斥体,.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)特定的工具,使后台线程休眠直到满足某些条件而没有定期唤醒并检查条件?