如何同步这个,很好吗?

时间:2014-06-04 02:34:55

标签: multithreading c++11 mutex condition-variable

给出以下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()调用时停止并无限期地等待。

我实际上还没有看到这种情况在实践中发生,但我想强化代码以防止可能发生。

1 个答案:

答案 0 :(得分:4)

您正在考虑这些可能性。但在这种情况下,您的代码是正确和安全的。

如果await在评估了条件测试之后立即被抢占,并且恰好在cv.wait()调用实际挂起线程之前,并且如果此时调用了countDown函数,则后者线程将在尝试获取block互斥锁时阻止,直到await实际调用cv.wait(lk)

cv.wait(lk)的调用会隐式释放block上的锁定,因此现在另一个线程可以获取blockcountDown()的锁定。只要线程持有block countDown()中的锁(即使在调用cv.notify_all()之后),await线程也无法从cv.wait()返回。 await线程在从block返回时尝试重新锁定cv.wait()时会隐式阻止。

<强>更新

我在<{1}}审核您的代码时 犯了一个菜鸟错误。

<blush>可能会虚假地返回 。也就是说,即使没有得到通知,它也可能返回。为防止出现这种情况,您应将cv.wait(lk)置于while循环之下,而不是if:

wait

现在,如果等待是虚假返回,它会重新检查条件,如果仍然不满意,则再次等待。