给出以下C ++ 11代码片段:
#include <condition_variable>
#include <mutex>
std::mutex block;
long count;
std::condition_variable cv;
void await()
{
std::unique_lock<std::mutex> lk(block);
if (count > 0)
cv.wait(lk);
}
void countDown()
{
std::lock_guard<std::mutex> lk(block);
if (count > 0)
{
count--;
if (count==0) cv.notify_all();
}
}
如果不清楚我想要完成什么,我希望等待调用等待暂停调用线程,而count大于0,如果它已经减少到零,那么它不应该暂停。其他线程可能会调用countDown(),它将唤醒之前调用过await的所有线程。
上面的代码似乎适用于我尝试过的所有情况,但是我对它有这个令人烦恼的疑问,因为在我看来,如果调用await()的线程刚好发生,就会出现意外行为的可能性在评估了条件测试之后立即抢占,并且在cv.wait()调用实际挂起线程之前,如果此时调用countDown函数,并且count等于0,那么它将发出一个通知条件变量, IF 它实际上已经在等待...但是调用await的线程还没有命中cv.wait(),所以当调用await的线程恢复时,它在cv.wait()调用时停止并无限期地等待。
我实际上还没有看到这种情况在实践中发生,但我想强化代码以防止可能发生。
答案 0 :(得分:4)
您正在考虑这些可能性。但在这种情况下,您的代码是正确和安全的。
如果await
在评估了条件测试之后立即被抢占,并且恰好在cv.wait()调用实际挂起线程之前,并且如果此时调用了countDown函数,则后者线程将在尝试获取block
互斥锁时阻止,直到await
实际调用cv.wait(lk)
。
对cv.wait(lk)
的调用会隐式释放block
上的锁定,因此现在另一个线程可以获取block
中countDown()
的锁定。只要线程持有block
countDown()
中的锁(即使在调用cv.notify_all()
之后),await
线程也无法从cv.wait()
返回。 await
线程在从block
返回时尝试重新锁定cv.wait()
时会隐式阻止。
<强>更新强>
我在<{1}}审核您的代码时 犯了一个菜鸟错误。
<blush>
可能会虚假地返回 。也就是说,即使没有得到通知,它也可能返回。为防止出现这种情况,您应将cv.wait(lk)
置于while循环之下,而不是if:
wait
现在,如果等待是虚假返回,它会重新检查条件,如果仍然不满意,则再次等待。