关于condition_variable的问题,为什么condition_variable与mutex配对

问题描述 投票:0回答:1

我最近一直在学习

std::condition_variable
,只是有一些问题我无法理解。

Cppreference 和许多其他教程都给出了这样的示例:

std::mutex m;
std::condition_variable cv;

void worker_thread()
{
    // wait until main() sends data
    std::unique_lock lk(m);
    cv.wait(lk, []{ return ready; });
 
    // after the wait, we own the lock
    std::cout << "Worker thread is processing data\n";
    data += " after processing";
 
    // send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";
 
    // manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}
  1. 为什么
    condition_variable
    总是与
    mutex
    配对?我不明白为什么这里需要互斥锁。
  2. lambda 函数
    []{ return ready; }
    在这里做什么?
c++ mutex condition-variable
1个回答
0
投票

std::condition_variable
作为执行一些低级消息传递的工具而存在。 C++ std 库没有提供“信号量”或“门”或其他线程原语,而是提供了一个与硬件线程工作方式相匹配的低级原语,您可以使用它来实现其他线程原语。

std::condition_variable
提供了发生通知的钩子,而
std::mutex
提供了保护数据的钩子。 事实上,在实际硬件和操作系统提供通知原语的现实世界中,硬件提供的通知原语并不是 100% 可靠,因此您需要一些数据来备份您的通知系统。

具体而言,可能会出现虚假通知 - 可能会出现与向您的

std::condition_variable
(或其底层硬件/操作系统原语)发送通知的任何人不对应的通知。

因此,当您收到通知时,必须检查一些数据(以线程安全的方式)以确定该通知是否确实对应于消息。

结果是使用

std::condition_variable
的标准方法是拥有 3 个紧密相关的部分:

  1. std::mutex
    保护一些包含可能消息的数据。
  2. 用于传输通知的
    std::condition_variable
  3. 包含消息本身的数据。

一个非常简单的消息系统可能是一扇可以打开但永远不会关闭的门。 在这里,您的数据是一个

bool
,表示“门是否打开”。 当您打开门时,您可以修改该布尔值(以线程安全的方式)并通知等待门打开的任何人。 等待门打开的代码等待条件变量;当它醒来时,它会检查门是否打开,并且仅在门确实打开时才接受通知。 如果门关闭,它会认为唤醒是虚假的,并返回睡眠状态。

实际代码中:

struct Gate {

  void open() {
    auto l = lock();
    is_open = true;
    cv.notify_all();
  }

  void wait() const {
    auto l = lock();
    cv.wait(l, [&]{return is_open;});
  }

private:
  mutable std::mutex m;
  bool is_open = false;
  mutable std::condition_variable cv;

  auto lock() const { return std::unique_lock{m}; }
}

open
中,我们锁定互斥体(因为我们正在编辑共享状态 - 布尔值),我们编辑
bool is_open
来表示它已打开,然后我们通知每个正在等待它打开的人它确实已打开.

wait
方面,我们需要一个互斥体来调用
cv.wait
。 然后,我们等待,直到大门打开——
wait
的 lambda 版本为我们构建了一个循环,检查虚假唤醒并在发生时返回睡眠状态。

© www.soinside.com 2019 - 2024. All rights reserved.