我正在查看“原子操作库”,发现了原子“wait”和“notify_”方法的 C++20 新功能。我很好奇 std::condition_variable 的“wait”和“notify_”方法有什么区别。
std:atomic
wait
、notify_all
和notify_one
方法与条件变量的方法类似。它们允许通过使用更高效、更轻量的原子变量来实现以前需要条件变量的逻辑,而无需使用互斥体。
wait
函数会阻塞线程,直到原子对象的值变得与特定值不同。它需要一个参数来与原子对象的值进行比较。并且它反复执行:
- 如果值相等,它将阻塞线程,直到通知
或notify_one
,或者线程被虚假解锁。notify_all
- 否则返回。
注意:
wait
仅在值发生更改时才保证返回,即使底层实现虚假地解除阻塞。
您可以在这里找到实现:https://github.com/ogiroux/atomic_wait/。
按平台选择策略是这样的:
整体使用模式有差异。
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();