【问题标题】:memory fences/barriers in C++: does boost or other libraries have them?C++ 中的内存栅栏/障碍:boost 或其他库有吗?
【发布时间】:2011-04-20 13:35:38
【问题描述】:

这些天我正在阅读有关内存栅栏和屏障的信息,以作为同步多线程代码和避免代码重新排序的一种方式。

我通常在 Linux 操作系统下使用 C++ 开发,我大量使用 boost 库,但我找不到任何与之相关的类。你知道提升中是否存在栅栏的内存屏障,或者是否有办法实现相同的概念?如果没有,我可以看看什么好的图书馆?

【问题讨论】:

    标签: multithreading boost boost-thread memory-barriers


    【解决方案1】:

    在 boost 中还没有低级内存屏障,但是有一个提议的 boost.atomic 库可以提供它们。

    编译器提供自己的内部函数或库函数,例如 gcc 的 __sync_synchronize() 或 Visual Studio 的 _mm_mfence()。

    C++0x 库提供原子操作,包括std::atomic_thread_fence 形式的内存栅栏。尽管自 V4.4 以来 gcc 提供了各种形式的 C++0x 原子,但 V4.4 或 V4.5 都没有包含这种形式的栅栏。我的(商业)just::thread 库提供了 C++0x 原子的完整实现,包括 g++ 4.3 和 4.4 以及 Microsoft Visual Studio 2005、2008 和 2010 的栅栏。

    【讨论】:

    • 嗨!谢谢。提议?如果我没记错的话,你的意思是 boost.atomic 在 boost/interprocess/details 中,所以在进程间库中。我去看看……
    • Boost.Atomic 是一个新库,而不是 Boost.Interprocess 的子集。 boost.atomic 的目的是实现全套 C++0x 风格的原子类型,包括栅栏和atomic<T> 模板。 Boost.Interprocess 和其他一些 boost 库在特定类型上定义了特定的原子操作,这些操作只做他们需要的事情。
    【解决方案2】:

    需要内存屏障的地方是避免在 SMP 环境中使用内核同步机制 - 通常是出于性能原因。

    在任何内核同步操作(例如信号量、锁定和解锁 mutice)和内容切换中都存在隐式内存屏障,以防止数据一致性危害。

    我刚刚发现自己(适度地)需要可移植的内存屏障实现(ARM 和 x86),并且还发现 linux 源代码树是这方面的最佳来源。 Linux 具有 mb()、rmb() 和 wmb() 宏的 SMP 变体 - 在某些平台上,这会导致比非 SMP 变体更具体(并且可能成本更低)的障碍。
    这在 x86 尤其是 ARM 上似乎不是问题,尽管两者都以相同的方式实现。

    这是我从linux头文件中抄来的(适用于ARMv7和非古代x86/x64处理器)

    #if defined(__i386__ ) || defined(__x64__)
    
    #define smp_mb()    asm volatile("mfence":::"memory")
    #define smp_rmb()   asm volatile("lfence":::"memory")
    #define smp_wmb()   asm volatile("sfence" ::: "memory")
    #endif
    
    #if defined(__arm__)
    
    #define dmb() __asm__ __volatile__ ("dmb" : : : "memory")
    
    #define smp_mb()    dmb()
    #define smp_rmb()   dmb()
    #define smp_wmb()   dmb()
    #endif
    

    自然地,涉足内存屏障会带来随之而来的风险,即生成的代码实际上无法测试,并且任何由此产生的错误都将是模糊的并且难以重现竞争条件:/

    顺便说一句,Linux kernel documentation 中对内存屏障有很好的描述。

    【讨论】:

      【解决方案3】:

      有一个boost::barrier 类/概念,但它有点高级。说起来,为什么需要低级屏障?同步原语应该足够了,不是吗?他们应该在必要时直接或通过其他较低级别的原语间接使用内存屏障。

      如果您仍然认为您需要一个低级实现,我知道没有实现障碍的类或库,但 Linux 内核中有一些特定于实现的代码。在include/asm-{arch}/system.h 中搜索mb()、rmb() 或wmb()。

      【讨论】:

      • 嗨艾布!我现在无法告诉你为什么这些是必要的。我正在阅读有关“避免重新排序”问题的信息,并且看到了根本不使用任何锁的代码。我在这里查看cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html 作为起点,
      • 啊,我明白了。他们试图避免使整个方法同步(这是更简单但更慢的解决方案)。
      • 注意:“将语义更改为要求释放锁以成为完整的内存屏障会产生性能损失。”另一个更简单、更慢的解决方案在这里。据我所知(我太累了,抱歉)他们提出的解决方案将障碍与锁分开以提高性能。我认为您将需要一个低级内存屏障(即单个不可移植的指令)。
      【解决方案4】:

      现在,在 2019 年,几乎所有 C++ 标准库的实现都应该可以使用 C++11 围栏。标头为<atomic>。

      您可以致电std::atomic_thread_fence 发出围栏。简而言之:

      • std::atomic_thread_fence(std::memory_order_release); 保证没有存储操作移过调用。 (所有副作用都将对其他线程可见。)
      • std::atomic_thread_fence(std::memory_order_acquire); 保证在调用之前不移动任何加载操作。 (其他线程的所有副作用都将可见。)

      documentation有更多详情。

      【讨论】:

        猜你喜欢
        • 2021-03-19
        • 1970-01-01
        • 1970-01-01
        • 2013-12-25
        • 2022-09-23
        • 2016-01-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多