I have a question about the notify_one function. At the following code,
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
#include <mutex>
std::condition_variable cv;
std::mutex m;
bool ready = false;
void f()
{
std::unique_lock<std::mutex> lk(m);
std::cout << "get into wait, ready=" << ready << std::endl;
cv.wait(lk, []() { return ready; });
std::cout << "get out of wait, ready=" << ready << std::endl;
}
int main()
{
std::thread a(f);
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::unique_lock<std::mutex> lk(m, std::defer_lock);
if (lk.try_lock()) {
std::cout << "main try_lock success, ready=" << ready << std::endl;
ready = true;
}
}
std::cout << "main notify, ready=" << ready << std::endl;
cv.notify_one();
// std::cout << "hello" << std::endl;
{
std::unique_lock<std::mutex> lk(m, std::defer_lock);
if (lk.try_lock()) {
std::cout << "main try_lock success, ready=" << ready << std::endl;
ready = true;
}
}
std::cout << "main notify, ready=" << ready << std::endl;
cv.notify_one();
a.join();
return 0;
}
I get a following result,
get into wait, ready=0
main try_lock success, ready=0
main notify, ready=1
main try_lock success, ready=1
main notify, ready=1
get out of wait, ready=1
But I expect a below result, because, according to the page, if notify_one is called wait is unblocked and lock of mutex(m) is reacquired.
get into wait, ready=0
main try_lock success, ready=0
main notify, ready=1
main notify, ready=1
get out of wait, ready=1
I found if I comment out the std::cout << "hello" << std::endl;
I get expected result. Looks to me like notify_one does not immediately unblock the wait. It is correct?
Thank you very much!
Looks to me like notify_one does not immediately unblock the wait. It is correct?
Notification immediately unblocks the wait, meaning the wait is able to resume after the notification. That is, the sleeping thread is marked as runnable.
However, the scheduler doesn't necessarily re-start it immediately. If it would have to pre-empt some already running thread/process, it will probably wait until a yield, a syscall or some other cancellation point, and otherwise not even look at the newly-runnable thread until the end of the current timeslice.
Unblocking something is not the same as forcing an immediate context switch (which is lucky, as otherwise synchronization would be even more expensive).