简体   繁体   English

C ++ condition_variable wait_for立即返回

[英]C++ condition_variable wait_for returns instantly

This is simple code from http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/ 这是来自http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/的简单代码

Why does wait_for() return instantly if I comment line with starting thread? 如果我用起始线程注释行,为什么wait_for()立即返回?

Like this: 像这样:

// 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;
}

Update: 更新:

Please don't look for other problems in this example (related buffering cout ...). 请不要在此示例中寻找其他问题(相关的缓冲cout ...)。 The original question was about why wait_for is skipped. 最初的问题是关于为什么wait_for被跳过。

Short answer: compile with -pthread and your issue will go away. 简短答案:使用-pthread编译,您的问题将消失。

Update: 更新:

This is a confirmed bug/issue in libstdc++. 这是libstdc ++中已确认的错误/问题。 Without -pthread being passed in as a compiler flag, the timed wait call will return immediately. 如果不将-pthread作为编译器标志传入,则定时等待调用将立即返回。 Given the history of the issue (3 years), it's not likely to be fixed anytime soon. 考虑到该问题的历史(3年),它不太可能很快得到解决。 Anyway, read my message below on why you should be using condition variables with predicates to avoid the spurious wakeup problem. 无论如何,请阅读以下我的信息,内容为何您应该使用带有谓词的条件变量来避免虚假唤醒问题。 It still holds true even if you are linking with the posix threads library. 即使您要与posix线程库链接,它仍然适用。

That sample code on cplusplus.com has several issues. cplusplus.com上的该示例代码存在多个问题。 For starters, amend this line: 对于初学者,请修改此行:

std::cout << '.';

To be like this: 要这样:

std::cout << '.';
std::cout.flush()

Otherwise, you won't see any dots if stdout isn't getting flushed. 否则,如果没有清除标准输出,您将看不到任何圆点。

If you compile your program (with the thread commented out) like this: 如果您像这样编译程序(在线程被注释掉的情况下):

g++ yourcode.cpp -std=c++11

Then the resulting a.out program exhibits the issue you described when the thread is not used. 然后,生成的a.out程序会出现您在不使用线程时描述的问题。 That is, there's a spurious wakeup when the thread is not used. 也就是说,不使用线程时会有虚假的唤醒。 It's like there's a phantom notify() call being invoked on the condition variable from some unknown source. 就像有一个幽灵的notify()调用正在从某个未知来源的条件变量上调用。 This is odd, but not impossible. 这很奇怪,但并非不可能。

But as soon as you uncomment out the declaration of the thread variable, the program will throw an exception (and crash) as a result of the program not using a multithreaded: 但是,一旦取消注释线程变量的声明,由于程序未使用多线程,程序将引发异常(并崩溃):

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)

Interesting, so let's fix that by recompiling with -pthread 有趣的是,让我们通过使用-pthread重新编译来解决此问题。

g++ yourcode.cpp -std=c++11 -pthread

Now everything works as expected with or without the thread. 现在,无论有没有线程,一切都能按预期工作。 No more spurious wakeup it seems. 似乎没有更多的虚假唤醒。

Now let's talk about why you are seeing the behavior you are seeing. 现在,让我们谈谈为什么您看到自己的行为。 Programs using condition variables should always be written to deal with spurious wakeup. 使用条件变量的程序应始终编写为处理虚假唤醒。 And preferably, use a predicate statement. 并且最好使用谓词语句。 That is, you might get a phantom notify causing your wait or wait_for statement to return early. 也就是说,您可能会收到幻像通知,从而导致您的wait或wait_for语句提前返回。 The example code on the web from cplusplus.com doesn't use a predicate nor does it deal with this possibility. 来自cplusplus.com的Web上的示例代码不使用谓词,也不处理这种可能性。

Let's amend it as follows: 让我们对其进行如下修改:

Change this block of code: 更改此代码块:

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

To be this: 要这样:

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

And then elsewhere outside of main , but after the declaration of value , add this function: 然后在main之外的其他地方,但是在value声明之后,添加以下函数:

bool condition_check() {
   return (value != 0);
}

Now the wait loop will wake up every second and/or when the notify call is made by the input thread. 现在,等待循环将每秒和/或在输入线程进行notify调用时唤醒。 The wait loop will continue until value != 0 . 等待循环将继续直到value != 0为止。 (Technically, value should be synchronized between threads, either with the lock or as a std::atomic value, but that's a minor detail). (从技术上讲, value应该在线程之间同步,可以使用锁或std :: atomic值,但这只是次要的细节)。

Now the mystery is why does the non-predicate version of wait_for suffer from the spurious wake_up problem. 现在有一个谜,就是为什么非谓语版的wait_for会遭受虚假的wake_up问题。 My guess is that's an issue with the single threaded C++ runtime that goes away when the multithreaded runtime ( -pthread ) is used. 我的猜测是,单线程C ++运行时的问题在使用多线程运行时( -pthread )时就消失了。 Perhaps condition_variable has different behavior or a different implementation when the posix thread library is linked in. 当posix线程库被链接时, condition_variable可能具有不同的行为或不同的实现。

There are several issue with this code: 此代码有几个问题:

First, as you have noticed, the program has to be build with the -pthread option. 首先,您已经注意到,该程序必须使用-pthread选项进行构建。

Second, you need to flush the output if you want to see the dots printed. 其次,如果要查看打印的点,则需要刷新输出。

Most importantly, this is entirely incorrect usage of mutex and condition variable. 最重要的是,这完全是互斥量和条件变量的不正确用法。 A condition variable notification indicates a change of value in a user-specified predicate/condition: the changing of the condition and examining it must be atomic and serialized: otherwise there is a data race and the behavior of the program would be undefined. 条件变量通知指示用户指定的谓词/条件中值的更改:条件的更改和检查必须是原子的且已序列化:否则,将发生数据争用并且程序的行为将不确定。

As is the case with the example program: value is read and written by two threads, but without any concurrency control mechanism, or to put it differently, there's no "happens-before" relation between the operation, which reads value and the operation which writes value . 与示例程序一样: value是由两个线程读写的,但是没有任何并发​​控制机制,或者换句话说,在读取value和要读取value的操作之间没有“先于先后”的关系。写value

Fixed example follows: 固定示例如下:

// 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;
}

So, what are the changes: 那么,有哪些变化:

  • mutex is moved to global scope (for the sake of the example), so the thread, which reads value can lock it, in order to modify value . 互斥锁已移至全局范围(为示例起见),因此读取value的线程可以将其锁定,以修改value
  • the read is in a separate variable; 读取在单独的变量中; it cannot be directly into value , because value must be modified only under the protection of the mutex, but holding the mutex, while waiting from input form std::cin would prevent the main thread from printing dots, as it will try to acquire the mutex upon timeout. 它不能直接转换为value ,因为value只能在互斥锁的保护下进行修改,但是在从输入格式std::cin等待时,保持互斥锁会阻止主线程打印点,因为它将尝试获取点。超时时互斥。
  • after each dot output, the std::cout is flushed 在每个点输出之后,刷新std::cout

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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