当前位置: 首页 > 知识库问答 >
问题:

原子等待操作是如何工作的?

欧阳楚
2023-03-14

启动C 20时,原子的操作有等待操作和通知操作。但我不知道它们到底是怎么工作的。cppreference说:

执行原子等待操作。表现为它重复执行以下步骤:

  • 比较此的值表示形式-

这些函数保证仅在值发生更改时返回,即使底层实现错误地解除了阻塞。

我不太明白这两个部分是如何相互关联的。这是否意味着如果值没有更改,那么即使我使用了notify\u one()/notify\u all()方法,函数也不会返回?这意味着该操作在某种程度上等于以下伪代码?

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

共有1个答案

赵俊晤
2023-03-14

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

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

类似这样:


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

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

 类似资料:
  • 问题内容: 我已经读到,使用原子包类使我们能够做到线程安全,减少代码锁定。但是我不确定原子包类中的方法如何在不使用锁或任何syncize关键字的情况下如何提供线程安全性。任何帮助将不胜感激。 问题答案: 他们使用非常低级的指令,例如Compare和Swap,以及sun.misc.Unsafe类中的多种其他方法。 基本上,像这样的方法调用将对应于唯一的处理器指令,从而消除了许多多线程问题。

  • 该程序创建一个线程,用dir()读取目录,并将文件放在通道上$N个工作线程读取该通道并“处理”(打印)文件。 然而,我得到了这个“等待的第一个操作:”错误。 关于这个错误,我已经在陷阱页面上读了好几遍了,但还是没有弄明白。能解释一下这里发生了什么吗? 目录内容: 运行程序: 程序travers-dir. p6:

  • 应用程序具有上下文路径-->/spring-form-simple-project 因此,为了访问,我使用: 这个控制器又返回student.jsp,当提交这个student.jsp时,它用-->@RequestMapping(value=“/AddStudent”,method=RequestMethod.post)调用controller 任何关于这通常如何工作的指示都将是有帮助的。 谢谢!

  • 问题内容: 如何更改以下代码,以触发两个异步操作并有机会同时运行? 我需要做这样的事情吗? 问题答案: TL; DR 不要在获得承诺的问题中使用模式,而是分别等待它们;而是使用(至少现在): 虽然您的解决方案 确实 并行运行这两个操作,但如果两个诺言都被拒绝,它就无法正确处理拒绝。 细节: 您的解决方案并行运行它们,但始终等待第一个完成,然后再等待第二个。 如果您只想启动它们,并行运行它们,并获得

  • 问题内容: 我如何更改以下代码,以触发两个异步操作并有机会同时运行? 我需要做这样的事情吗? 问题答案: TL; DR 不要在获得承诺的问题中使用模式,而是分别等待它们;而是使用(至少现在): 虽然您的解决方案确实并行运行这两个操作,但是如果两个诺言都被拒绝,它就无法正确处理拒绝。 细节: 您的解决方案并行运行它们,但始终等待第一个完成,然后再等待第二个。如果您只想启动它们,并行运行它们,并获得两

  • 我正在用C编写一个程序。为了简单起见,我们可以说:有几个变量,许多线程都可以读写。每次写入其中一个时,它都是通过原子交换(GCC原子操作、同步和交换)写入的。我是否需要在每次读取其中一个变量时使用原子负载,或者原子写入是否足以避免在写入过程中读取数据? 注意,需要使用其中一个变量的数据的任何位置都会首先复制值: 我的问题不是关于数据竞赛,也就是说我不担心我会丢失数据。我担心的是,在我阅读它的过程中