2016-12-30 195 views
1

返回这是http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/C++ 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

似乎不启动程序它的工作原理确定之前automalicaly负载并行线程lib中,与LD_PRELOAD =/lib目录/ x86_64的-Linux的GNU/libpthread.so.0。 任何建议,为什么会发生? – Yuri

+2

显示用于构建的命令行。显示运行的输出。 –

回答

0

有几个问题与此代码:

首先,因为你已经注意到,该方案必须与-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的同时,将阻止主线程打印点,因为它将在超时时尝试获取互斥锁。
  • 每个点输出,输出后std::cout刷新
+0

在调用'cv.wait_for()'时释放互斥锁。 – chill

+0

然而,正如@selbie指出的那样,虚假唤醒存在一个问题(潜力,我没有观察到它)。 – chill

4

简短的回答:编译-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)的问题。也许condition_variable在posix线程库链接时具有不同的行为或不同的实现。

+0

即使我添加-lpthread也会出现同样的问题。即使我添加了-lpthread,链接器也不会链接pthread库。但是,如果我用LD_PRELOAD pthread运行程序,它按预期工作。似乎这是与我的工具链版本/配置有关的问题。使用相同的工具链版本测试两台机器。 (g ++(Ubuntu 5.4.0-6ubuntu1〜16.04.4)5.4.0 20160609 ) – Yuri