2

I think the source code below may not end forever.

Assume that all waiter threads have entered the wait state. After that, waker call notify_all()

One thread, t1, wakes up and does something. And Other threads wake up while working, but mutex is occupied by t1, so it returns to the wait state.

That other thread falls asleep forever, so this program doesn't end.

#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>

using namespace std;

mutex m;
condition_variable cv;

bool isStop = false;

void waiter()
{
    unique_lock<mutex> ul(m);
    cv.wait(ul, []() { return isStop; });

    // ...do something...
}

void waker()
{
    this_thread::sleep_for(chrono::seconds(1));

    unique_lock<mutex> ul(m);
    isStop = true;
    ul.unlock();

    cv.notify_all();
}

void main()
{
    vector<thread> ts;
    for (int i = 0; i < 100; ++i)
        ts.emplace_back(waiter);

    thread w(waker);

    for (auto& t : ts)
        t.join();
    w.join();
}

so I think that waiter function need to Unlock and notify

void waiter()
{
    unique_lock<mutex> ul(m);
    cv.wait(ul, []() { return isStop; });

    // ...do something...

    ul.unlock();
    cv.notify_all();
}

Do you think I'm right?

17
  • Neither worker nor waiter should call unlock. Otherwise there is a chance that there's none of conditional variable waiters. Commented Dec 31, 2024 at 18:55
  • "That other thread falls asleep forever". No. Other threads are blocked on the mutex acquired by the first waiter thread to wake up. Eventually, that first thread will release the mutex, and then some other thread will acquire it. And so they will go and "do something", one by one. Those threads are no longer waiting on the condition variable, so there's no point calling notify_all again. Commented Dec 31, 2024 at 18:55
  • @igor-tandetnik thank you i thought a thread go back to wait if fail to lock. i'm studying c++ concurrency, how can i find details, i couldn't condition_variable specification in c++ refrence Commented Dec 31, 2024 at 19:13
  • @3CxEZiVlQ i think waker() need to unlcok otherwise, After cv.notify_all in waker(), the waiter wakes up but isStop is False, so waiter can go back to wait state again Commented Dec 31, 2024 at 19:18
  • 1
    @PepijnKramer Even if waker starts first, runs to completion, and terminates before any waiters get to start, the program is still correct. That'd mean that, by the time waiter checks isStop for the first time, it's already true and so the thread never blocks on the condition variable and proceeds straight to "do something". Commented Dec 31, 2024 at 21:14

1 Answer 1

4

The original code is correct. When you call notify_all all waiting threads are notified, they'll all attempt to re-aquire the mutex, check the predicate, then continue. This might take a while as only one thread can aquire the mutex at a time but it's guaranteed that all threads will eventually be woken up once the mutex is released. If the thread can't immediately aquire the mutex it'll simply block on aquiring the mutex, it won't re-enter the waiting state without checking the predicate first.

Sign up to request clarification or add additional context in comments.

2 Comments

thank you so muck! i thought thread came back to wait even if fail to aquire the mutex
There is really no such thing as "fail to acquire a mutex". A thread would just block, for as long as it takes, until the mutex becomes available. Unless you are thinking of try_lock; but a condition variable doesn't do that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.