C++ condition_variable wait_for立即返回

Yur*_*uri 1 c c++ linux pthreads c++11

这是来自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;
}
Run Code Online (Sandbox Code Playgroud)

更新:

请不要在这个例子中寻找其他问题(相关的缓冲cout ...).最初的问题是为什么跳过wait_for.

sel*_*bie 7

简短的回答:编译,-pthread你的问题将消失.

更新:这是libstdc ++中已确认的错误/问题.如果不-pthread作为编译器标志传入,则定时等待调用将立即返回.鉴于该问题的历史(3年),它不太可能很快修复.无论如何,请阅读下面的消息,说明为什么你应该使用带有谓词的条件变量来避免虚假的唤醒问题.即使您使用posix线程库进行链接,它仍然适用.

cplusplus.com上的示例代码有几个问题.对于初学者,修改此行:

std::cout << '.';
Run Code Online (Sandbox Code Playgroud)

这样:

std::cout << '.';
std::cout.flush()
Run Code Online (Sandbox Code Playgroud)

否则,如果没有刷新stdout,你将看不到任何点.

如果您编译您的程序(线程已注释掉),如下所示:

g++ yourcode.cpp -std=c++11
Run Code Online (Sandbox Code Playgroud)

然后生成的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)
Run Code Online (Sandbox Code Playgroud)

有意思,所以让我们通过重新编译来解决这个问题 -pthread

g++ yourcode.cpp -std=c++11 -pthread
Run Code Online (Sandbox Code Playgroud)

现在,无论是否有线程,一切都按预期工作.似乎没有更多的虚假唤醒.

现在让我们谈谈你为什么看到你所看到的行为. 应始终编写使用条件变量的程序来处理虚假唤醒. 最好使用谓词语句.也就是说,您可能会获得一个幻像通知,导致您的wait或wait_for语句提前返回.来自cplusplus.com的网络上的示例代码不使用谓词,也不处理这种可能性.

我们将其修改如下:

更改此代码块:

  while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
    std::cout << '.';
  }
Run Code Online (Sandbox Code Playgroud)

为此:

 while (cv.wait_for(lck,std::chrono::seconds(1), condition_check)==false) {
   std::cout << '.';
   std::cout.flush();
 }
Run Code Online (Sandbox Code Playgroud)

然后在其他地方之外main,但在声明之后value,添加此功能:

bool condition_check() {
   return (value != 0);
}
Run Code Online (Sandbox Code Playgroud)

现在等待循环将每秒唤醒和/或当notify输入线程进行调用时.等待循环将继续,直到value != 0.(从技术上讲,value应该在线程之间进行同步,使用锁或作为std :: atomic值,但这是一个小细节).

现在的谜团是为什么非谓词版本wait_for遭受虚假的wake_up问题.我的猜测是单线程C++运行时的问题在使用多线程运行时(-pthread)时会消失.condition_variable当链接posix线程库时,可能有不同的行为或不同的实现.