Why condition_variable is waiting for the lock in producer-consumer? C++
Asked Answered
P

1

6

See the following classical producer-consumer code:

int main()
{
    std::queue<int> produced_nums;
    std::mutex m;
    std::condition_variable cond_var;
    bool done = false;
    bool notified = false;

    std::thread producer([&]() {
        for (int i = 0; i < 5; ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
            std::unique_lock<std::mutex> lock(m);
            std::cout << "producing " << i << '\n';
            produced_nums.push(i);
            notified = true;
            cond_var.notify_one();
        }

        done = true;
        cond_var.notify_one();
    });

    std::thread consumer([&]() {
        std::unique_lock<std::mutex> lock(m);
        while (!done) {
            while (!notified) {  // loop to avoid spurious wakeups
                cond_var.wait(lock);
            }
            while (!produced_nums.empty()) {
                std::cout << "consuming " << produced_nums.front() << '\n';
                produced_nums.pop();
            }
            notified = false;
        }
    });

    producer.join();
    consumer.join();
}

I have copied this from cppreference.

Everything is pretty much straightforward to me, except the line in the consumer:

cond_var.wait(lock); 

I do understand the loop that waits cond_var to be notified, but why is it waiting for the lock?

Plenum answered 21/5, 2018 at 7:57 Comment(4)
What do you mean by "waiting for the lock"? cond_var.wait(lock); in fact releases the mutex m locked in the constructor of lock before waiting. Then, if the consumer thread gets notified, m is locked again internally, so the thread can safely use produced_nums container.Randolf
so it releases the lock for the producer could continue the loop?Plenum
See en.cppreference.com/w/cpp/thread/condition_variable/wait, I think the description if pretty clear.Randolf
The lock is required set to begin the wait, and is atomically(as far as you're concerned) released at the point of waiting. It is the stock model for pthread cond-vars and mutexes. The posted code is broken btw. done can be set without protection,The last iteration sets notified and signals, then releases the mutex. then the consumer picks up the mutex, eventually clears notified, circles around to check done (which may still not be set by the producer, but is about to be, unprotected), then falls back into waiting for a notified that will never come.Chincapin
I
6

cond_var.wait(lock); does not wait for the lock. That line does 3 things

  1. It unlocks the lock variable
  2. It waits until someone signals the condition.
  3. it locks the lock variable again before it returns,

It does all this atomically. While the thread is waiting for the condition variable, the mutex is not locked - that way your producer thread can acquire the lock and safely set any variables shared between the consumers/producer.

It locks the mutex again upon return, so the consumer again can safely access the shared variables.

If you tried to manage locking/unlocking the mutex yourself, you would end up with race conditions betwen locking/unlocking the mutex and waiting/signalling the condition variable - this is why waiting for a condition variable is tied to a mutex - so it can be done atomically, without race conditions.

Inspectorate answered 21/5, 2018 at 8:8 Comment(3)
what does it mean it returns?Plenum
@EduardRostomyan In "it returns," the "it" refers to the functin wait.Leuctra
oook, now i see the picture, so it just unlock to get notified and lock again if not, am I correct?Plenum

© 2022 - 2024 — McMap. All rights reserved.