[英]C++ condition_variable wait_for returns instantly
这是来自http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/的简单代码
如果我用起始线程注释行,为什么wait_for()立即返回?
像这样:
// condition_variable::wait_for example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <chrono> // std::chrono::seconds
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable, std::cv_status
std::condition_variable cv;
int value;
void read_value() {
std::cin >> value;
cv.notify_one();
}
int main ()
{
std::cout << "Please, enter an integer (I'll be printing dots): ";
//std::thread th (read_value);
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
std::cout << '.';
}
std::cout << "You entered: " << value << '\n';
//th.join();
return 0;
}
更新:
请不要在此示例中寻找其他问题(相关的缓冲cout ...)。 最初的问题是关于为什么wait_for被跳过。
简短答案:使用-pthread
编译,您的问题将消失。
这是libstdc ++中已确认的错误/问题。 如果不将-pthread
作为编译器标志传入,则定时等待调用将立即返回。 考虑到该问题的历史(3年),它不太可能很快得到解决。 无论如何,请阅读以下我的信息,内容为何您应该使用带有谓词的条件变量来避免虚假唤醒问题。 即使您要与posix线程库链接,它仍然适用。
cplusplus.com上的该示例代码存在多个问题。 对于初学者,请修改此行:
std::cout << '.';
要这样:
std::cout << '.';
std::cout.flush()
否则,如果没有清除标准输出,您将看不到任何圆点。
如果您像这样编译程序(在线程被注释掉的情况下):
g++ yourcode.cpp -std=c++11
然后,生成的a.out程序会出现您在不使用线程时描述的问题。 也就是说,不使用线程时会有虚假的唤醒。 就像有一个幽灵的notify()
调用正在从某个未知来源的条件变量上调用。 这很奇怪,但并非不可能。
但是,一旦取消注释线程变量的声明,由于程序未使用多线程,程序将引发异常(并崩溃):
terminate called after throwing an instance of 'std::system_error'
what(): Enable multithreading to use std::thread: Operation not permitted
Please, enter an integer (I'll be printing dots): Aborted (core dumped)
有趣的是,让我们通过使用-pthread
重新编译来解决此问题。
g++ yourcode.cpp -std=c++11 -pthread
现在,无论有没有线程,一切都能按预期工作。 似乎没有更多的虚假唤醒。
现在,让我们谈谈为什么您看到自己的行为。 使用条件变量的程序应始终编写为处理虚假唤醒。 并且最好使用谓词语句。 也就是说,您可能会收到幻像通知,从而导致您的wait或wait_for语句提前返回。 来自cplusplus.com的Web上的示例代码不使用谓词,也不处理这种可能性。
让我们对其进行如下修改:
更改此代码块:
while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
std::cout << '.';
}
要这样:
while (cv.wait_for(lck,std::chrono::seconds(1), condition_check)==false) {
std::cout << '.';
std::cout.flush();
}
然后在main
之外的其他地方,但是在value
声明之后,添加以下函数:
bool condition_check() {
return (value != 0);
}
现在,等待循环将每秒和/或在输入线程进行notify
调用时唤醒。 等待循环将继续直到value != 0
为止。 (从技术上讲, value
应该在线程之间同步,可以使用锁或std :: atomic值,但这只是次要的细节)。
现在有一个谜,就是为什么非谓语版的wait_for
会遭受虚假的wake_up问题。 我的猜测是,单线程C ++运行时的问题在使用多线程运行时( -pthread
)时就消失了。 当posix线程库被链接时, condition_variable
可能具有不同的行为或不同的实现。
此代码有几个问题:
首先,您已经注意到,该程序必须使用-pthread
选项进行构建。
其次,如果要查看打印的点,则需要刷新输出。
最重要的是,这完全是互斥量和条件变量的不正确用法。 条件变量通知指示用户指定的谓词/条件中值的更改:条件的更改和检查必须是原子的且已序列化:否则,将发生数据争用并且程序的行为将不确定。
与示例程序一样: value
是由两个线程读写的,但是没有任何并发控制机制,或者换句话说,在读取value
和要读取value
的操作之间没有“先于先后”的关系。写value
。
固定示例如下:
// condition_variable::wait_for example
#include <chrono> // std::chrono::seconds
#include <condition_variable> // std::condition_variable, std::cv_status
#include <iostream> // std::cout
#include <mutex> // std::mutex, std::unique_lock
#include <thread> // std::thread
std::mutex mtx;
std::condition_variable cv;
int value;
void read_value() {
int v;
std::cin >> v;
std::unique_lock<std::mutex> lck(mtx);
value = v;
cv.notify_one();
}
int main() {
std::cout << "Please, enter an integer (I'll be printing dots): ";
std::thread th(read_value);
std::unique_lock<std::mutex> lck(mtx);
while (cv.wait_for(lck, std::chrono::seconds(1)) == std::cv_status::timeout) {
std::cout << '.' << std::flush;
}
std::cout << "You entered: " << value << '\n';
th.join();
return 0;
}
那么,有哪些变化:
value
的线程可以将其锁定,以修改value
。 value
,因为value
只能在互斥锁的保护下进行修改,但是在从输入格式std::cin
等待时,保持互斥锁会阻止主线程打印点,因为它将尝试获取点。超时时互斥。 std::cout
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.