12

考虑以下代码:

struct payload
{
    std::atomic< int > value;
};

std::atomic< payload* > pointer( nullptr );

void thread_a()
{
    payload* p = new payload();
    p->value.store( 10, std::memory_order_relaxed );
    std::atomic_thread_fence( std::memory_order_release );
    pointer.store( p, std::memory_order_relaxed );
}

void thread_b()
{
    payload* p = pointer.load( std::memory_order_consume );
    if ( p )
    {
        printf( "%d\n", p->value.load( std::memory_order_relaxed ) );
    }
}

C++ 是否对线程 a 中的栅栏与线程 b 中的消费操作的交互做出任何保证?

我知道在这个例子中我可以用 store-release 替换栅栏 + atomic store 并让它工作。但我的问题是关于使用围栏的这种特殊情况。

阅读标准文本,我可以找到关于释放栅栏与获取栅栏的交互以及释放栅栏与获取操作的交互的条款,但没有关于释放栅栏和消费操作的交互的条款。

我认为,用获取替换消耗将使代码符合标准。但据我了解处理器实现的内存排序约束,我应该只需要线程 b 中较弱的“消耗”排序,因为内存屏障强制线程 a 中的所有存储在存储到指针之前可见,并且读取有效负载取决于从指针读取。

标准一致吗?

4

2 回答 2

3

您的代码有效。

我知道在这个例子中我可以用 store-release 替换栅栏 + atomic store 并让它工作。但我的问题是关于使用围栏的这种特殊情况。

具有宽松原子操作的栅栏比相应的原子操作强。例如(来自http://en.cppreference.com/w/cpp/atomic/atomic_thread_fence,注释):

虽然原子存储释放操作可防止所有先前的写入移过存储释放,但具有memory_order_release排序的atomic_thread_fence可防止所有先前的写入移过所有后续存储。

于 2015-06-20T09:46:08.037 回答
0

尽管这显然是意图,但指定栅栏和原子操作的交互方式意味着仅官方支持列出的组合。(这种规范不仅冗长,难以阅读,更难转化为有效的直觉,很容易变得不完整。)

我在标准中看不到任何支持将消费操作与释放屏障配对的标准,即使正常实现不可能不支持,除非在全局程序优化期间通过特殊努力来检测特定用例并故意破坏它

于 2019-05-25T14:24:52.307 回答