c++multithreadingc++11condition-variable

Correct way to set ready flag for std::condition_variable


I have two threads where one thread needs to wait for the other thread to complete its task. To solve this problem I decided to use std::condition_variable.

My question is, should the thread that is executing the task acquire the same mutex that was used in std::condition_variable if the variable that is checked in the callback of std::condition_variable is atomic i.e. std::atomic<bool>?

In other words, in the code below, should the lock std::lock_guard<std::mutex> lock(mtx); be uncommented or not?

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <chrono>

std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> ready = false;

void worker() {
    std::unique_lock<std::mutex> lock(mtx);
    std::cout << "Waiting for signal...\n";
    cv.wait(lock, [] { return ready.load(); });
    std::cout << "Continue\n";
}

void signaler() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    {
        //std::lock_guard<std::mutex> lock(mtx);
        ready = true;
        std::cout << "Data ready\n";
    }
    cv.notify_one(); 
}

int main() {

    std::thread t1(worker);
    std::thread t2(signaler);

    t1.join();
    t2.join();

    return 0;
}

Solution

  • Yes, you absolutely must lock the mutex, even if the variable is atomic. At the top of condition_variable we have:

    Even if the shared variable is atomic, it must be modified while owning the mutex to correctly publish the modification to the waiting thread.

    The mutex not only protects the variable, but also prohibits that the condition variable can wake up and check the condition.

    If the mutex is not locked, it is possible that the condition variable wakes up, then checks the condition and finds it false. If the other thread can now switch the condition to true and send the notification, before the condition variable releases the lock and waits, the condition variable has missed the notification.

    When the text in notify_one says "The notifying thread does not need to hold the lock on the same mutex as the one held by the waiting thread(s)", then this means only the call of that function, not the point where the condition is changed.