如何修复条件变量等待/通知的竞争条件

Posted

技术标签:

【中文标题】如何修复条件变量等待/通知的竞争条件【英文标题】:How to fix race condition for condition variable wait/notify 【发布时间】:2017-07-27 20:54:07 【问题描述】:

这个问题的答案是错误的,因为它有可能陷入僵局。 Condition Variable - Wait/Notify Race Condition

我没有找到解决竞争条件或死锁问题的解决方案。

假设我们有两个线程。现在的目标如下。

first condition:
Thread 1 Waits
Thread 2 Notifies

second condition:
Thread 2 Notifies
Thread 1 Should not wait and continue normal execution.

如何在没有通知队列的情况下正确实现这一点?因为我希望这部分代码尽可能快地运行并使用布尔值而不是将项目添加到队列中。而且只有 2 个线程,所以使用队列对我来说似乎有点过分了。

存在竞争条件时的伪代码:

Thread 1:
lock(x);
if(!signaled)

    unlock(x); // ********
    // still small gap, how to avoid?
    cv.wait(); // forget spurious wakeup for sake of simplicity 
    signaled = false;

else // ********
    unlock(x);

Thread 2:
lock(x);
signaled = true;
cv.notify();
unlock(x);

现在,如果您删除带有 ******** 注释的两行,竞争条件将得到解决,并且在线程 1 在拥有锁时等待而线程 2 卡在锁定 x 处时,将引入死锁的机会。

【问题讨论】:

您可能想研究使用std::atomic 机制来处理这种情况。 您的假设不正确。 conditional_variable::wait 原子地进入等待模式并释放锁。 即使conditional_variable::wait 释放锁,我很确定它会在 等待之前这样做,所以内部仍然存在差距@SergeyA @M.kazemAkhgary,不,你错了。没有差距,通过执行来保证。这就是它的存在 【参考方案1】:

你困惑的根源是你对条件变量的误解。它们的固有特征是锁释放和进入等待模式是原子地完成的,即两者之间没有任何事情发生。

所以在变量进入等待模式之前不能对变量进行任何修改,因为锁不会被释放。这是任何符合conditional_variable 实现的基本保证,例如,这里是std::conditional_variable 参考页面的摘录:

http://en.cppreference.com/w/cpp/thread/condition_variable/wait

原子地释放锁,阻塞当前正在执行的线程,然后 将其添加到等待 *this 的线程列表中。线程将是 在执行 notify_all() 或 notify_one() 时解除阻塞。它也可能 被虚假地疏通。畅通无阻时,无论出于何种原因, 重新获取锁并等待退出。如果这个函数通过 例外,锁也被重新获得。 (直到 C++14)

【讨论】:

好的,忘记我之前的评论,如果两个操作以原子方式完成,那么问题就解决了。你能参考你的说法吗? 我会在你使用的任何 API 中。如果您使用的是 C++,它将在 conditional_variable::wait 的文档中。会给出答案。

以上是关于如何修复条件变量等待/通知的竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

通知条件变量并解锁关联互斥锁后的“数据竞争”(不是真的)

Java并发程序设计(16)并发锁之条件变量

线程同步之条件锁

基于条件变量的消息队列

四十Linux 线程——线程同步之条件变量

条件变量和互斥锁的使用