这是来自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。
答案 0 :(得分:6)
简短回答:使用-pthread
进行编译,您的问题就会消失。
更新:This is a confirmed bug/issue in libstdc++.如果没有-pthread
作为编译器标志传入,则定时等待调用将立即返回。鉴于该问题的历史(3年),它不太可能很快修复。无论如何,请阅读下面的消息,说明为什么你应该使用带有谓词的条件变量来避免虚假的唤醒问题。即使您使用posix线程库进行链接,它仍然适用。
cplusplus.com上的示例代码有几个问题。对于初学者,修改这一行:
std::cout << '.';
是这样的:
std::cout << '.';
std::cout.flush()
否则,如果没有刷新stdout,你将看不到任何点。
如果您编译程序(线程已注释掉),请执行以下操作:
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的网络上的示例代码不使用谓词,也不处理这种可能性。
让我们修改如下:
更改此代码块:
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
的非谓词版本会受到虚假唤醒问题的影响。我的猜测是单线程C ++运行时的问题在使用多线程运行时(-pthread
)时消失了。当posix线程库链接时,condition_variable
可能有不同的行为或不同的实现。
答案 1 :(得分:0)
此代码存在以下问题:
首先,正如您所注意到的,该程序必须使用OutOfMemoryError
选项构建。
其次,如果要查看打印的点,则需要刷新输出。
最重要的是,这完全是错误的互斥和条件变量的使用。条件变量通知指示用户指定的谓词/条件中的值的更改:条件的更改和检查它必须是原子的和序列化的:否则存在数据争用并且程序的行为将是未定义的。
与示例程序的情况一样:-pthread
由两个线程读取和写入,但没有任何并发控制机制,或者换句话说,操作之间没有“事先发生”关系,其中读取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
被刷新