条件变量并不总是有效

Posted

技术标签:

【中文标题】条件变量并不总是有效【英文标题】:condition_variable not always working 【发布时间】:2016-02-27 13:50:01 【问题描述】:

似乎condition_variable notify_one 并不总是按应有的方式工作。

struct Task 
    std::mutex mutex;
    std::condition_variable cv;
    std::atomic_bool launchfalse;
;
void job(Task& task) 
    std::unique_lock<std::mutex> locktask.mutex;
    task.cv.wait(lock, [&] return task.launch == true; );

int main() 
    for (auto i=0 ; i<1000*1000 ; i++) 
        Task task;
        std::thread threadjob, std::ref(task);
        task.launch = true;
        task.cv.notify_one();
        thread.join();
    

这个程序几乎永远不会到达终点,它在绝大多数情况下都永远停止在循环中。 为什么会这样?

【问题讨论】:

会发生什么,您预计会发生什么?这些是一个好问题的基本部分...... 谢谢@UlrichEckhardt,我希望程序能够完成,但它几乎永远不会完成。 它到底挂在哪里?哪一行是最后成功执行的? 【参考方案1】:

这里有两个错误:

如果您同步访问某个对象,则不需要该对象的原子类型。您的 atomic_bool 只会造成开销。 如果您想同步访问launch 标志,您需要在写入之前锁定它的互斥锁。你不会在main() 中这样做。

解释:

    main() 创建 task main() 创建 thread job() 锁定互斥体 job() 检查launch,这是错误的 main() 设置launch main() 发送简历,没人收到 由于步骤 4 中 launch 的值,job() 等待 CV

通常,第 3 步和第 6 步将是原子的,因为在没有锁定互斥锁的情况下,任何其他线程都不应该接触launch。由于没有发生这种情况,因此允许发生依赖操作的交错,这最终导致了意外行为。

【讨论】:

所以具体来说,job() 在第 6 步等待 CV 的 原因 是因为 main 函数没有将写入同步到 launch 和所以线程不一定能看到变化?如果线程认为launch 为真,那么它永远不会阻塞条件变量,因为condition_variable::wait 在等待之前会检查谓词。 想一想,也许我的解释是错误的,@SteveJessop。此外,你的也没有改进它,因为atomic&lt;bool&gt; 应该让它变得安全。我将添加另一个解释。 这就是我一直在努力解决的问题。我现在认为我的问题的答案是“不”。 atomic 类型意味着 CV 上的阻塞按照main 写入launch 的顺序排列。但是在main 通知 CV 之后,仍然没有任何东西可以对 CV 上的线程阻塞进行排序。 为什么在第 6 步期间没有人收到?是的,job()4) 期间检查过,但它仍在这个条件变量中等待 等待带有谓词回调 (lambda) 的 CV 首先检查谓词(步骤 4)。然后,它以原子方式解锁 CV 上的互斥锁和块(步骤 7)。在第 4 步之后它不会被阻止。这是有效的,因为条件受互斥锁保护,除非有人(在本例中为 main())违反合同并在没有持有互斥锁的情况下访问数据。【参考方案2】:

不清楚你想要什么,但你的问题是主线程可能在线程有时间调用wait之前实现launch=truenotify_one()。在这种情况下,您应该知道notify 没有延迟,因此您的主线程将在join 上被阻塞,而线程在wait 上被阻塞。

【讨论】:

以上是关于条件变量并不总是有效的主要内容,如果未能解决你的问题,请参考以下文章

再谈条件变量—从入门到出家

removeFromSuperview() 并不总是有效

条件变量和互斥锁的使用

信号量互斥锁和条件变量的区别

Pandas read_hdf() 在有效 where 条件下返回“无效变量引用”

linux的线程的同步手段 — 条件变量