繁体   English   中英

C ++ condition_variable wait_for立即返回

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM