条件变量锁定应覆盖condition_variable.notify_all吗?

时间:2018-02-24 01:43:52

标签: c++ multithreading

我已经实现了一个允许我使用条件变量同步线程的类。我发现有关notify_all是在锁内还是在锁之外完成的冲突信息。我找到了两种方式构建的例子。

首先释放锁的参数是防止等待线程在被通知释放后立即阻塞互斥锁。

反对释放锁定的论点是一个断言,等待线程可能会错过通知。

发布功能的两个版本在这里:

// version 1 - unlock then notify.
void release(int address = 1)
{
    {
        std::lock_guard<std::mutex> lk(_address_mutex);
        _address = address;
    }
    _cv.notify_all();
}

// version 2 - notify then unlock
void release(int address = 1)
{
    std::lock_guard<std::mutex> lk(_address_mutex);
    _address = address;
    _cv.notify_all();
}

作为参考,等待代码如下所示:

bool wait(const std::chrono::microseconds dur, int address = 1)
{
    std::unique_lock<std::mutex> lk(_address_mutex);
    if (_cv.wait_for(lk, dur, [&] {return _address == address; }))
    {
        _address = 0;
        return true;
    }
    return false;
}

在版本1中是否存在等待线程丢失通知的风险,其中允许互斥锁在notify_all之前超出范围?如果是这样,它是如何发生的? (我不明白这会导致错过通知。)

我可以清楚地看到在通知期间如何锁定互斥锁会导致等待的线程立即进入等待状态。但如果这可以防止错过通知,这是一个很小的代价。

1 个答案:

答案 0 :(得分:5)

如果在条件测试更改状态和thr通知状态之间的某个时间间隔内保持互斥锁,则释放锁没有风险。

{
    std::lock_guard<std::mutex> lk(_address_mutex);
    _address = address;
}
_cv.notify_all();

此处,_address更改后,互斥锁已解锁。所以没有风险。

如果我们将_address修改为原子,那么这看起来是正确的:

{
    std::lock_guard<std::mutex> lk(_address_mutex);
}
_address = address;
_cv.notify_all();

但事实并非如此;在这里,互斥锁在修改条件测试和通知之间的整个时间段内被释放,

_address = address;
{
    std::lock_guard<std::mutex> lk(_address_mutex);
}
_cv.notify_all();

然而,上述情况再次变得正确(如果有点奇怪的话)。

风险是条件测试将使用互斥锁激活(为假),然后更改,然后发送通知,然后等待线程等待通知并释放互斥锁。< / p>

waiting|signalling
lock
test
        test changed
        notification
listen+unlock

以上是错过通知的示例。

只要我们在测试更改后和通知之前将互斥锁保留在任何地方,就不会发生。