条件变量和无锁容器

Condition variables and lockfree container

本文关键字:变量 条件      更新时间:2023-10-16

条件变量使用互斥锁,.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,很明显互斥锁必须是访问队列时被锁定。但是我们假设不是使用std::queue,而是使用microsoft并发::concurrent_queue from PPL。成员函数如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)特定的工具,使后台线程睡眠,直到某些条件满足,没有定期唤醒和检查条件?

代码在不同的场景下是不正确的,例如。如果在计算const bool was_empty = the_queue.empty();时队列中只有一个元素,但是一个线程正在消耗该元素,而另一个线程试图消耗该元素并等待该条件,则写入器在将元素插入队列后不会通知该线程。

关键问题是,一个接口中的所有操作都是线程安全的这一事实并不一定意味着你对接口的使用是安全的。如果依赖于自动执行的多个操作,则需要在外部提供同步机制。

有没有其他的,也许是操作系统(在我的情况下:Windows)特定的工具,这会使后台线程休眠,直到满足某些条件没有定期醒来检查情况吗?

这就是事件的作用

但如果你只针对Windows平台(Vista+),你应该查看
精简读写器(SRW)锁