条件变量和无锁容器

3

条件变量使用互斥锁和.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)特定的工具,使后台线程睡眠直到满足某些条件而不时常唤醒并检查条件?

2个回答

1
在不同的场景下代码是不正确的,例如,如果队列在评估const bool was_empty = the_queue.empty();时只有一个元素,但线程消耗了该元素,另一个线程试图使用并等待条件,那么写入者将在将元素插入队列后不会通知该线程。
关键问题在于接口中所有操作都是线程安全的并不一定意味着您使用接口是安全的。如果您依赖于多个操作以原子方式执行,则需要提供外部同步机制。

虽然我已经选择了你的答案,但还有一个问题。如果没有共享状态,即我不会检查the_queue.empty()而只是始终调用cond.notify(),那么这是否意味着我实际上可以安全地使用实际上不锁定任何内容的本地互斥量?当然,这意味着我会尝试过于频繁地唤醒后台任务,但是当没有人等待条件变量时,通知也可能不会造成任何伤害,对吧? - user2460318

1

是否有其他特定于操作系统(在我的情况下是Windows)的工具,可以使后台线程休眠,直到满足某些条件,而不需要定期唤醒并检查条件?

这正是Events的用途

但如果您只针对Windows平台(Vista+),您应该查看Slim Reader/Writer (SRW) Locks


一些时间过去了,现在我可以给你的答案点赞了。所以我现在这样做了。再次感谢。 - user2460318

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接