Mutex protecting std::condition_variable

后端 未结 2 538
暖寄归人
暖寄归人 2021-01-14 08:52

Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread. Any threa

相关标签:
2条回答
  • 2021-01-14 09:17

    The mutex-protected flag must be set, and the condition variable get signalled, while the mutex is still being held:

    {
        std::lock_guard<std::mutex> lock(m);
        proceed = true;
        cv.notify_one();
    }
    

    Furthermore, in this case the proceed flag does not need to be an atomic entity. A simple

    bool proceed;
    

    will be sufficient. With access to proceed happening only while holding the associated mutex, making proceed atomic accomplishes absolutely nothing.

    atomic entities are for handling exotic concurrency situations that do not involve any mutexes in the first place.

    0 讨论(0)
  • 2021-01-14 09:37

    I don't think Sam`s answer is correct. Consider the following code:

    // thread #1:
    std::unique_lock<std::mutex> l(m);
    while (!proceed) cv.wait(l);
    
    // thread #2:
    proceed = true; // atomic to avoid data race
    cv.notify_one();
    

    The problem here is the following possible sequence of events:

    thread #1: while (!proceed) // evaluated as true
    thread #2: proceed = true;
    thread #2: cv.notify_one();
    thread #1: cv.wait(l); // never gets notified
    

    To avoid this scenario, a typical solution is to protect modification of proceed with the same mutex:

    // thread #1:
    std::unique_lock<std::mutex> l(m);
    while (!proceed) cv.wait(l);
    
    // thread #2:
    {
       std::lock_guard<std::mutex> l(m);
       proceed = true; // does not need to be atomic
    }
    cv.notify_one();
    

    Now, proceed = true; must happen either before while (!proceed) or after cv.wait(l); starts waiting; both is ok. In the first case, there is no waiting at all; in the second case, cv.notify_one(); is guaranteed to happen only when cv.wait(l); is actually waiting.

    Now, what about your (kind-of academic) case?

    // thread #1:
    std::unique_lock<std::mutex> l(m);
    while (!proceed) cv.wait(l);
    
    // thread #2:
    proceed = true; // atomic to avoid data race
    {
       std::lock_guard<std::mutex> lock(m);
    }
    cv.notify_one();
    

    I believe this case is also perfectly valid, since the above-described wrong scenario cannot happen as well. For simple reason. If while (!proceed) is evaluated as false, again, there is no waiting. And, if while (!proceed) is evaluated as true, then notification cannot happen until cw.wait(l); is invoked.

    Conclusion

    I believe your first example is ok and the quote from cppreference is incorrect.

    0 讨论(0)
提交回复
热议问题