获取释放内存顺序与顺序一致性不同的实际示例是什么?
What's are practical example where acquire release memory order differs from sequential consistency?
显然,顺序一致原子操作的有效可观察行为与有效C++程序中仅获取发布的操作不同。定义在C++标准中给出(从C++11开始)或在此给出。
然而,我从未遇到过一个真实世界中的算法或数据结构示例,其中获取发布语义不足,需要顺序一致性。
什么是现实世界算法或数据结构的实用示例,其中需要顺序一致性,而获取释放内存顺序是不够的?
注意,即使是std::mutex
也不能保证序列一致性。
Peterson的算法是一个需要序列一致性的例子。
在互斥之前的日子里,该算法被用来让单个线程访问受保护的区域。该算法只适用于2个线程,每个线程管理一个表示访问保护区意图的标志。如果双方都在(大约)同一时间设置了标志,双方都会后退并重试。真正的算法更先进,因为它使用"turn"标志来管理公平访问,但为了显示seq/cst和acq/rel之间的差异,这是不必要的。
下面是Peterson算法的一个易于编译的简化版本,它实际上表明,如果使用比序列一致性弱的东西,算法就会被破坏。有趣的是,这在X86上是收支平衡的,因为该平台允许重新排序存储加载
存储加载重新排序的问题是,两个线程都可能通过将其me
标志设置为true
来表达其访问受保护区域的意图,而同时都从him
标志读取false
(因为该值尚未传播到两个线程)并进入受保护区域。这在顺序一致性的情况下是不可能的。
使用gcc
,我必须使用-O3
优化进行编译,才能激发assert
,而使用clang
则不是必需的。两个编译器都使用不同的方法来实现顺序一致性。
#include <thread>
#include <atomic>
#include <assert.h>
std::atomic<bool> flag1{false};
std::atomic<bool> flag2{false};
std::atomic<int> counter{0};
// Change these to memory_order_seq_cst to fix the algorithm
static const auto store_ordering = std::memory_order_release;
static const auto load_ordering = std::memory_order_acquire;
void busy(int n)
{
auto &me = (n==1) ? flag1 : flag2;
auto &him = (n==1) ? flag2 : flag1;
for (;;)
{
for (;;)
{
me.store(true, store_ordering);
if (him.load(load_ordering) == false)
{
// got the 'lock'
break;
}
// retention, no wait period -> busy loop
me.store(false, store_ordering);
}
int tmp = counter.fetch_add(1, std::memory_order_relaxed);
assert(tmp == 0);
/*
* critical area
*/
tmp = counter.fetch_sub(1, std::memory_order_relaxed);
assert(tmp == 1);
me.store(false, store_ordering);
}
}
int main()
{
std::thread t1{busy, 1};
std::thread t2{busy, 2};
t1.join();
t2.join();
}
相关文章:
- 计算 I+V[i++] 的顺序是什么?
- C++:使用方法调用析构函数的顺序是什么?
- 函数模板实例化、替换和重载解析的顺序是什么?
- 在 OpenGL 中计算矩阵时,转换的正确顺序是什么?
- 正确的包含顺序是什么
- 抛出多个异常时,catch 块执行的顺序是什么,为什么?
- 编译器在遇到提取或插入运算符时处理信息(字符串、操纵器等)的顺序是什么?
- 在C++中为临时库调用析构函数的顺序是什么
- 操作员优先c |进行计算的确切顺序是什么
- 在虚拟继承中构造函数调用的顺序是什么
- 函数参数的销毁顺序是什么
- 函数参数的销毁顺序是什么
- 通过构造函数初始化集初始化的变量的销毁顺序是什么
- 基类的销毁顺序是什么
- openGL:使用glm进行转换的顺序是什么
- 在C++中,析构函数的调用顺序和成员变量的销毁顺序是什么
- 类成员中的初始化顺序是什么
- 类中成员的最佳顺序是什么
- JUCE原子设施的记忆顺序是什么?
- if括号if(..)中语句的求值顺序是什么