条件变量并不总是有效
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<bool>
应该让它变得安全。我将添加另一个解释。
这就是我一直在努力解决的问题。我现在认为我的问题的答案是“不”。 atomic 类型意味着 CV 上的阻塞按照main
写入launch
的顺序排列。但是在main
通知 CV 之后,仍然没有任何东西可以对 CV 上的线程阻塞进行排序。
为什么在第 6 步期间没有人收到?是的,job()
在4)
期间检查过,但它仍在这个条件变量中等待
等待带有谓词回调 (lambda) 的 CV 首先检查谓词(步骤 4)。然后,它以原子方式解锁 CV 上的互斥锁和块(步骤 7)。在第 4 步之后它不会被阻止。这是有效的,因为条件受互斥锁保护,除非有人(在本例中为 main()
)违反合同并在没有持有互斥锁的情况下访问数据。【参考方案2】:
不清楚你想要什么,但你的问题是主线程可能在线程有时间调用wait
之前实现launch=true
和notify_one()
。在这种情况下,您应该知道notify
没有延迟,因此您的主线程将在join
上被阻塞,而线程在wait
上被阻塞。
【讨论】:
以上是关于条件变量并不总是有效的主要内容,如果未能解决你的问题,请参考以下文章