【问题标题】:Using reference_wrapper as condition_variable predicate使用 reference_wrapper 作为 condition_variable 谓词
【发布时间】:2014-09-15 18:31:06
【问题描述】:

注意:以下内容同样适用于 Boost.Thread 和 C++11 线程。

我有一个条件变量,它实际上是一个简单的布尔变量。

// assume these are global
mutex m;
condition_variable c;
boolean b = false;

我想使用wait(lock, predicate) 语法。例如,我可以使用 lambda:

c.wait(lock, [] () { return b; });

但我认为应该有一种更惯用的方式将变量包装为可调用对象。所以我发现reference_wrapper 提供了一个operator() 来检索包装的值。因此我尝试了:

c.wait(lock, cref(b));

但是 g++ (4.9.1) 没有编译它,争论no matching function for call to '(boost::reference_wrapper<const bool>) ()' (如果我使用std::ref 错误有些不同,但仍然没有编译)。

reference_wrapper 不应该作为条件变量的正确谓词吗?如果不是,为什么?在这种情况下,b 的正确包装是什么?

编辑:所以@Praetorian 已经非常正确地解释了我的错误,但是,在 Boost 或标准上真的没有这样的东西(这里可能有错误):

template<typename T>
struct as_callable {
    T &objref;
    as_callable(T &r) : objref(r) {}
    T &operator()() {
        return objref;
    }
};

【问题讨论】:

  • std::ref 返回一个引用包装器。 Boost Phoenix ref 返回一个 functor。我想你可能混淆了这两者(见我的回答)
  • while (!b) { c.wait(lock); }c.wait(lock, []{ return b; }); 更具可读性,并且语义相同。当 b 是需要捕获的本地 (c.wait(lock, [&amp;]{ return b; });) 或成员变量 (c.wait(lock, [this]{ return b; });) 时,lambda 变得更糟糕,但显式版本保持不变。

标签: c++ multithreading c++11 boost reference-wrapper


【解决方案1】:

std::reference_wrapper::operator() 仅在 reference_wrapper 存储 callable 时可用,而普通的 bool 则不可用。谓词使用 lamdba。

如果该布尔值在与您的条件变量等待它的线程不同的线程中被修改,还可以考虑使用std::atomic_bool 而不是普通的bool


如果您真的想使用包装器而不是 lambda,您可以编写一个简单的包装器类,它重载 operator() 并在调用时返回存储的布尔值。

struct bool_wrapper
{
    bool_wrapper(bool& b) : b_(&b) {}
    bool *b_;

    bool operator()() const noexcept { return *b_; }
};

现在您可以使用这个类来包装布尔值并将其传递给condition_variable::wait as

c.wait(lock, bool_wrapper(b));

Live demo

【讨论】:

  • 好的,我明白了。我现在已经扩展了这个问题(如果没有这样的事情,我会把你的问题标记为正确的)。如果对b 的访问受到锁保护,我不应该需要std::atomic_bool,对吧?
  • @javidcf reference_wrapper 确实为T&amp; 定义了一个隐式转换运算符,但这里的问题是condition_variable::wait 需要一个返回bool 的空谓词,所以它会尝试调用无论你传递给它什么,就好像它是一个可调用的。您需要的是一个包装器,它存储T 并提供一个operator(),它只返回相同的T。我不知道有任何这样的包装器,而且听起来也不是很有用,是吗?你可以自己写一个。如果您通过互斥锁保护对布尔值的访问,则不需要atomic_bool
  • @javidcf 添加了一个示例,但如果我是你,我会使用 lambda。
  • 好的,谢谢,我明白了。我并不是讨厌 lambdas 或其他任何东西,但我很好奇标准中是否已经涵盖了它。
【解决方案2】:

除了其他答案,它恰当地解释了哪里出了问题,我想我可能能够解释如何你会得出错误的期望。

你瞧,还有 Boost Phoenix 的 ref()cref() 版本,令人惊讶的是,它以 Phoenix Lazy Actor 的形式提供了参考。即:phx::ref()phx::cref()函子

这意味着您可以简单地使用

c.wait(lock, phx::cref(b));

你可能在过去的某个地方遇到过。

【讨论】:

  • 哇,我从没听说过,但我确实是这么想的。
猜你喜欢
  • 2013-10-20
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-23
  • 1970-01-01
  • 2020-08-05
  • 1970-01-01
相关资源
最近更新 更多