std::原子等待操作是如何工作的

How std::atomic wait operation works?

本文关键字:工作 何工作 等待 操作 std      更新时间:2024-09-23

启动C++20时,std::atomicwait()notify_one()/notify_all()操作。但我不知道它们应该如何工作。cppreference说:

执行原子等待操作。表现得好像在重复执行以下步骤:

  • 比较此的值表示形式->装载(订单(旧的。
    • 如果等于,则阻塞,直到*this被notify_one((或notify_all((通知,或者线程被错误地解除阻塞
    • 否则,返回

这些函数只有在值发生更改时才保证返回,即使底层实现被错误地解锁

我不清楚这两个部分是如何相互关联的。这是否意味着,如果if的值没有更改,那么即使我使用notify_one()/notify_all()方法,函数也不会返回?意味着操作在某种程度上等于下面的伪代码?

while (*this == val) {
// block thread
}

是的,正是这样。notify_one/all只是为等待的线程提供了一个检查值是否更改的机会。如果它保持不变,例如,因为不同的线程已将该值设置回其原始值,则该线程将保持阻塞状态。

注意:此代码的有效实现是使用互斥和condition_variables的全局数组。原子变量通过指针通过散列函数映射到这些对象。这就是为什么你会得到虚假的唤醒。一些原子共享相同的条件变量。

类似这样的东西:


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();
}

真正的实现可能会使用操作系统原语,例如Windows上的WaitForAddress或Linux上的futex(至少对于int大小的类型(。