请考虑以下代码:
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中的fence的交互与线程b中的consume操作有任何保证?
我知道在这个例子中我可以用商店版本替换fence + atomic存储并让它工作。但我的问题是关于使用栅栏的这种特殊情况。
阅读标准文本我可以找到关于释放围栏与获取围栏的交互以及具有获取操作的释放围栏的交互的子句,但没有关于释放围栏和使用操作的交互的内容。
我认为用一个获取取代消费将使代码符合标准。但据我了解处理器实现的内存排序约束,我应该只需要线程b中较弱的“消耗”排序,因为内存屏障强制线程a中的所有存储在存储到指针之前可见,并且读取有效负载取决于指针的读取。
标准是否同意?
答案 0 :(得分:2)
您的代码有效。
我知道在这个例子中我可以用商店版本替换fence + atomic存储并让它工作。但我的问题是关于使用栅栏的这种特殊情况。
放松原子操作的栅栏比相应的原子操作更强。例如。 (来自http://en.cppreference.com/w/cpp/atomic/atomic_thread_fence,注释):
虽然原子存储释放操作会阻止所有先前的写操作移过存储版本,但 atomic_thread_fence 具有 memory_order_release 排序会阻止所有先前的写操作移过所有后续写入存储
答案 1 :(得分:0)
尽管这显然是其意图,但是指定围栏和原子操作的交互方式意味着仅正式支持列出的组合。 (这种规范风格不仅冗长,难以阅读,甚至难以转化为有效的直觉,而且很容易造成不完整。)
我没有看到标准支持将消费操作与发布障碍配对,即使正常实现不可能也不支持,除非在全局程序优化期间进行了特别的努力以发现特定用例并故意破坏它。