c++ - 忙等待循环中是否需要内存屏障或原子操作?

标签 c++ multithreading gcc memory-barriers spinlock

考虑下面的 spin_lock() 实现,最初来自 this answer :

void spin_lock(volatile bool* lock)  {  
    for (;;) {
        // inserts an acquire memory barrier and a compiler barrier
        if (!__atomic_test_and_set(lock, __ATOMIC_ACQUIRE))
            return;

        while (*lock)  // no barriers; is it OK?
            cpu_relax();
    }
}

我已经知道的:

  • volatile 防止编译器在 while 循环的每次迭代中优化出 *lock 重新读取;
  • volatile inserts neither memory nor compiler barriers ;
  • 这样的实现实际上在 GCC 中适用于 x86(例如在 Linux 内核中)和其他一些架构;
  • 至少有一个内存和编译器屏障is required在通用架构的 spin_lock() 实现中;此示例将它们插入 __atomic_test_and_set() .

问题:

  1. 这里的 volatile 是否足够,或者是否有任何架构或编译器在 while 循环中需要内存或编译器屏障或原子操作?

    1.1 根据C++标准?

    1.2 在实践中,对于已知的架构和编译器,特别是 GCC 及其支持的平台?

  2. 这个实现在 GCC 和 Linux 支持的所有架构上是否安全? (在某些架构上至少效率低下,对吧?)
  3. 根据 C++11 及其内存模型,while 循环是否安全?

有几个相关的问题,但我无法从它们中构建出明确而明确的答案:

最佳答案

这很重要:在 C++ 中,volatile 与并发性完全没有关系! volatile 的目的是告诉编译器它不应优化对受影响对象的访问。它确实告诉 CPU 任何事情,主要是因为 CPU 已经知道内存是否是 volatilevolatile 的目的是有效地处理内存映射 I/O。

C++ 标准在第 1.10 节 [intro.multithread] 中非常清楚,对在一个线程中修改并在另一个线程中访问(修改或读取)的对象的非同步访问是未定义的行为。避免未定义行为的同步原语是库组件,如原子类或互斥锁。该子句仅在信号的上下文中(即,作为 volatile sigatomic_t)和向前进展的上下文中(即,线程最终将执行具有可观察到的效果,例如访问 volatile 对象或执行 I/O)。没有提到与同步结合使用的 volatile

因此,对跨线程共享的变量的不同步评估会导致未定义的行为。是否声明为 volatile 与这种未定义的行为无关。

关于c++ - 忙等待循环中是否需要内存屏障或原子操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32677667/

相关文章:

C++ 构建器模式插入运算符 `<<` 样式

c++ - C++ 中 vector 的内部工作?

c++ - 可移植的 c++ 对齐?

c++ - static for operator new 是什么意思

c# - 建议数据结构/同步方法

c - GCC的函数指针,分配地址

c++ - 如何读取nm命令: What does nm options -T and -U (undefined) mean?

multithreading - Python 3.5 asyncio 从不同线程中的同步代码在事件循环上执行协程

python - CPU 和 GPU 操作并行化

c++ - ARM neon 优化 - 摆脱多余的负载