condition_variable::notify_one 不立即解封等待?
condition_variable::notify_one does not instantly unblock wait?
我对 notify_one 函数有疑问。在以下代码中,
#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;
}
我得到以下结果,
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
但我希望得到下面的结果,因为根据 page,如果调用 notify_one,等待将被解除阻塞并重新获取 mutex(m) 的锁。
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
我发现如果我注释掉 std::cout << "hello" << std::endl;
我会得到预期的结果。在我看来 notify_one 不会立即解除等待。正确吗?
非常感谢!
在您的第一个 cv.notify_one();
之后,您没有同步点(直到 a.join();
)并且线程已经 运行ning 很可能会继续 运行 并到达 a.join();
在调度程序决定让另一个线程旋转之前。
作为实验,您可以通过在通知后稍微休眠来让出线程的执行槽 - 它可能导致输出的顺序符合您的预期。
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
不过不要在生产代码中依赖它。这不能保证任何事情,因为线程之间没有同步(join()
除外)。
Looks to me like notify_one does not immediately unblock the wait. It is correct?
立即通知取消阻止等待,这意味着等待能够在通知后恢复。即休眠线程被标记为可运行。
但是,调度程序不一定会立即重新启动它。如果它必须抢占一些已经 运行 thread/process,它可能会等到 yield、系统调用或其他一些取消点,否则甚至不会查看新运行的线程,直到当前时间片结束。
解除阻塞与强制立即切换上下文不同(幸运的是,否则同步会更加昂贵)。
我对 notify_one 函数有疑问。在以下代码中,
#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;
}
我得到以下结果,
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
但我希望得到下面的结果,因为根据 page,如果调用 notify_one,等待将被解除阻塞并重新获取 mutex(m) 的锁。
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
我发现如果我注释掉 std::cout << "hello" << std::endl;
我会得到预期的结果。在我看来 notify_one 不会立即解除等待。正确吗?
非常感谢!
在您的第一个 cv.notify_one();
之后,您没有同步点(直到 a.join();
)并且线程已经 运行ning 很可能会继续 运行 并到达 a.join();
在调度程序决定让另一个线程旋转之前。
作为实验,您可以通过在通知后稍微休眠来让出线程的执行槽 - 它可能导致输出的顺序符合您的预期。
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
不过不要在生产代码中依赖它。这不能保证任何事情,因为线程之间没有同步(join()
除外)。
Looks to me like notify_one does not immediately unblock the wait. It is correct?
立即通知取消阻止等待,这意味着等待能够在通知后恢复。即休眠线程被标记为可运行。
但是,调度程序不一定会立即重新启动它。如果它必须抢占一些已经 运行 thread/process,它可能会等到 yield、系统调用或其他一些取消点,否则甚至不会查看新运行的线程,直到当前时间片结束。
解除阻塞与强制立即切换上下文不同(幸运的是,否则同步会更加昂贵)。