std::atomic 和 std::condition_variable wait、notify_* 方法之间的区别

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

我正在查看“原子操作库”,发现了原子“wait”和“notify_”方法的 C++20 新功能。我很好奇 std::condition_variable 的“wait”和“notify_”方法有什么区别。

c++ c++20 condition-variable stdatomic
2个回答
19
投票

std:atomic

wait
notify_all
notify_one
方法与条件变量的方法类似。它们允许通过使用更高效、更轻量的原子变量来实现以前需要条件变量的逻辑,而无需使用互斥体。

wait
函数会阻塞线程,直到原子对象的值变得与特定值不同。它需要一个参数来与原子对象的值进行比较。并且它反复执行

  • 如果值相等,它将阻塞线程,直到通知
    notify_one
    notify_all
    ,或者线程被虚假解锁。
  • 否则返回。

注意:

wait
仅在值发生更改时才保证返回,即使底层实现虚假地解除阻塞。


您可以在这里找到实现:https://github.com/ogiroux/atomic_wait/

按平台选择策略是这样的:

  • Linux:默认为 futex(有表),回退到 futex(无表)-> CVs -> 定时退避 -> spin。
  • Mac:默认为 CV(表格),回退到定时退避 -> 自旋。
  • Windows:默认为 futex(无表),回退到定时退避 -> spin。
  • CUDA:默认为定时退避,回退到旋转。 (这并不是在这棵树中全部签入的。)
  • 未识别平台:默认旋转。

10
投票

整体使用模式有差异。

condition_variable
等待需要互斥锁。在通知之前应使用相同的互斥锁:

std::mutex mtx;
std::condition_variable cv;

bool condition();
void change_condition();

...

std::unique_lock<std::mutex> lock(mtx);
while (!condition())
{
   cv.wait(lock);
}

...

std::unique_lock<std::mutex> lock(mtx);
change_condition();
lock.unlock();
cv.notify_one();

现在如果你有带有条件变量的原子,你仍然需要锁:

std::mutex mtx;
std::condition_variable cv;

std::atomic<bool> condition;

...

std::unique_lock<std::mutex> lock(mtx);
while (!condition.load())
{
   cv.wait(lock);
}

...

std::unique_lock<std::mutex> lock(mtx);
condition.store(true);
lock.unlock();
cv.notify_one();

原子本身不需要加锁保护,因此可以在不加锁的情况下进行修改。不过,仍然需要互斥锁来与等待同步,避免丢失唤醒。唤醒线程的替代方法如下:

condition.store(true);
std::unique_lock<std::mutex> lock(mtx);
lock.unlock();
cv.notify_one();

互斥锁定不能省略,即使在通知方也是如此。

(而且你无法摆脱

condiion_variable_any
和在
lock
/
unlock
中不执行任何操作的“空互斥体”)。


现在,原子等待。 除了其他答案中提到的没有虚假唤醒之外,不需要互斥体:


std::atomic<bool> condition;

...

condition.wait(false);

...

condition.store(true);
condition.notify_one();
© www.soinside.com 2019 - 2024. All rights reserved.