从 C++20 开始,std::atomic有wait()and notify_one()/notify_all()操作。但我不明白它们到底是如何工作的。cppreference 说:
执行原子等待操作。其行为就像重复执行以下步骤:
- 将 this->load(order) 的值表示与旧的值表示进行比较。
- 如果它们相等,则阻塞直到
*this收到notify_one()或notify_all()通知,或者线程被虚假地解除阻塞。- 否则,返回。
仅当值发生更改时,这些函数才保证返回,即使底层实现虚假地解除阻塞。
我不太明白这两个部分是如何相互关联的。这是否意味着如果值没有改变,那么即使我使用notify_one()/notify_all()方法,该函数也不会返回?这意味着该操作在某种程度上等于以下伪代码?
while (*this == val) {
// block thread
}
Run Code Online (Sandbox Code Playgroud)
是的,就是这样。notify_one/all 只是为等待线程提供检查值更改的机会。如果它保持不变,例如因为不同的线程已将该值设置回其原始值,则该线程将保持阻塞状态。
注意:此代码的有效实现是使用互斥体和条件变量的全局数组。然后原子变量通过哈希函数通过它们的指针映射到这些对象。这就是为什么你会得到虚假唤醒。一些原子共享相同的条件变量。
像这样的东西:
std::mutex atomic_mutexes[64];
std::condition_variable atomic_conds[64];
template<class T>
std::size_t index_for_atomic(std::atomic<T>* ptr) noexcept
{ return reinterpret_cast<std::size_t>(ptr) / sizeof(T) % 64; }
void atomic<T>::wait(T value, std::memory_order order)
{
if(this->load(order) != value)
return;
std::size_t index = index_for_atomic(this);
std::unique_lock<std::mutex> lock(atomic_mutexes[index]);
while(this->load(std::memory_order_relaxed) == value)
atomic_conds[index].wait(lock);
}
template<class T>
void std::atomic_notify_one(std::atomic<T>* ptr)
{
const std::size_t index = index_for_atomic(ptr);
/*
* normally we don't need to hold the mutex to notify
* but in this case we updated the value without holding
* the lock. Therefore without the mutex there would be
* a race condition in wait() between the while-loop condition
* and the loop body
*/
std::lock_guard<std::mutex> lock(atomic_mutexes[index]);
/*
* needs to notify_all because we could have multiple waiters
* in multiple atomics due to aliasing
*/
atomic_conds[index].notify_all();
}
Run Code Online (Sandbox Code Playgroud)
真正的实现可能会使用操作系统原语,例如 Windows 上的 WaitForAddress 或 Linux 上的(至少对于 int 大小的类型)futex。
| 归档时间: |
|
| 查看次数: |
1648 次 |
| 最近记录: |