C ++ condition_variable wait_for立即返回

时间:2016-12-30 10:29:28

标签: c++ c linux c++11 pthreads

这是来自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。

2 个答案:

答案 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

那么,有哪些变化:

  • mutex被移动到全局范围(为了示例),因此读取// 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被刷新