C++ 什么';一般来说,处理虚假唤醒的正确方法是什么?

C++ 什么';一般来说,处理虚假唤醒的正确方法是什么?,c++,c++11,condition-variable,spurious-wakeup,C++,C++11,Condition Variable,Spurious Wakeup,在下面的选项中,当使用条件变量时,有没有正确的方法来处理虚假唤醒 1) 将wait(unique_lock_ul)放入无限while循环,使用布尔值 unique_lock<mutex> ul(m); while(!full) cv.wait(ul); 如果这些都不正确,那么如何轻松处理虚假唤醒 我对C++中的条件变量很陌生,我不确定我读的一些代码是否处理了虚假唤醒的问题。 < P>简短的答案是,您的代码可能是正确的或错误的;您没有确切显示完整的是如何操作的 C++代码的各

在下面的选项中,当使用条件变量时,有没有正确的方法来处理虚假唤醒

1) 将
wait(unique_lock_ul)
放入无限
while
循环,使用布尔值

unique_lock<mutex> ul(m); 
while(!full)
  cv.wait(ul);
如果这些都不正确,那么如何轻松处理虚假唤醒


我对C++中的条件变量很陌生,我不确定我读的一些代码是否处理了虚假唤醒的问题。

< P>简短的答案是,您的代码可能是正确的或错误的;您没有确切显示
完整的
是如何操作的

<> C++代码的各个位从来都不是线程安全的。线程安全是代码的一个关系属性;如果两位代码永远不会导致竞争条件,那么它们彼此之间可以是线程安全的

但是一位代码永远不会是线程安全的;说某物是线程安全的就像说某物是“相同的高度”


“猴子见猴子做”条件变量模式如下:

template<class T>
class cv_bundle {
  std::mutex m;
  T payload;
  std::condition_variable cv;
public:
  explicit cv_bundle( T in ):payload(std::move(in)) {}

  template<class Test, class Extract>
  auto wait( Test&& test, Extract&& extract ) {
    std::unique_lock<std::mutex> l(m);
    cv.wait( l, [&]{ return test(payload); } );
    return extract(payload);
  }
  template<class Setter>
  void load( Setter&& setter, bool only_one = true ) {
    std::unique_lock<std::mutex> l(m);
    bool is_set = setter( payload );

    if (!is_set) return; // nothing to notify
    if (only_one)
      cv.notify_one();
    else
      cv.notify_all();
  }
};
模板
类cv_束{
std::互斥m;
T有效载荷;
std::条件变量cv;
公众:
显式cv_bundle(tin):有效负载(std::move(in)){
模板
自动等待(测试和测试、提取和提取){
std::唯一锁l(m);
等待(l,[&]{返回测试(有效载荷);});
返回提取(有效载荷);
}
模板
无效加载(Setter&&Setter,bool only\u one=true){
std::唯一锁l(m);
bool is_set=设定器(有效载荷);
如果(!is_set)return;//没有要通知的内容
如果(只有一个)
cv.通知_one();
其他的
cv.通知所有人();
}
};
test
接受一个
T&payload
,如果有东西要消耗,则返回true(即,唤醒不是虚假的)

extract
获取
T&payload
并从中返回您想要的任何信息。它通常应该重置有效载荷

setter
修改
T&payload
,使
test
返回
true
。如果这样做,则返回
true
。如果选择不执行,则返回
false

所有3个都在对
T有效负载的互斥锁访问中被调用

现在,您可以在此基础上生成变体,但这样做很难正确。例如,不要假设原子负载意味着不必锁定互斥锁

当我将这三件事捆绑在一起时,您可以对一堆条件变量使用一个互斥体,或者对不仅仅是条件变量使用互斥体。有效载荷可以是一个布尔、一个计数器、一个数据向量,或者其他更为陌生的东西;通常,它必须始终受到互斥的保护。如果它是原子的,则在修改的值和通知之间的打开间隔的某个时间点,必须锁定互斥锁,否则可能会丢失通知

手动循环控制而不是传递lambda是一种修改,但是描述哪种手动循环是合法的,哪种是竞争条件是一个复杂的问题

实际上,除非我有非常好的理由,否则我不会让这个猴子看到猴子做“货物崇拜”式的条件变量的使用。然后我不得不阅读C++内存和线程模型,这不能成为我的一天,这意味着我的代码很可能不正确。
请注意,如果传入的任何lambda进入并回调到
cv_bundle
中,我显示的代码将不再有效。

1或3种方式都可以处理虚假唤醒(假设
完全修改受同一互斥锁保护),除非谓词条件错误,否则应该是:

unique_lock<mutex> ul(m); 
cv.wait(ul, [&](){return full;});
唯一锁定ul(m);
cv.wait(ul,[&](){returnfull;});
使此代码等于变量1


与其他两种情况不同,变型2不正常,但不会重新检查伪唤醒等待条件。

1)
2)
是相同的代码。你是想展示一些不同的东西吗?是的,对不起,我刚修改过
template<class T>
class cv_bundle {
  std::mutex m;
  T payload;
  std::condition_variable cv;
public:
  explicit cv_bundle( T in ):payload(std::move(in)) {}

  template<class Test, class Extract>
  auto wait( Test&& test, Extract&& extract ) {
    std::unique_lock<std::mutex> l(m);
    cv.wait( l, [&]{ return test(payload); } );
    return extract(payload);
  }
  template<class Setter>
  void load( Setter&& setter, bool only_one = true ) {
    std::unique_lock<std::mutex> l(m);
    bool is_set = setter( payload );

    if (!is_set) return; // nothing to notify
    if (only_one)
      cv.notify_one();
    else
      cv.notify_all();
  }
};
unique_lock<mutex> ul(m); 
cv.wait(ul, [&](){return full;});