我正在阅读Anthony Williams的C ++ Concurrency in Action,并且不理解它对var global = {
homepage: '/list/',
urlRedirect: function (url) {
window.location.href = url;
},
xButtons: this.homepage,
url: this.urlRedirect('items')
};
console.log(global.xButtons); // gives me undefined
console.log(global.homepage); // gives /list/
global.url // urlRedirect is not a function
类的推送实现。清单7.12准确
lock_free_stack
因此想象两个线程(void push(T const& data)
{
counted_node_ptr new_node;
new_node.ptr=new node(data);
new_node.external_count=1;
new_node.ptr->next=head.load(std::memory_order_relaxed)
while(!head.compare_exchange_weak(new_node.ptr->next,new_node, std::memory_order_release, std::memory_order_relaxed));
}
,A
)调用push函数。它们都在循环中到达但不能启动它。所以他们都从B
读取相同的值。
然后我们会发生以下事情:
head.load(std::memory_order_relaxed)
线程因任何原因被删除B
线程启动循环,显然成功地向堆栈添加了一个新节点。A
线程重回正轨并开始循环。这就是我觉得有趣的地方。
因为有B
和std::memory_order_relaxed
的加载操作,如果成功,看起来线程之间没有任何同步。
我的意思是它就像compare_exchange_weak(..., std::memory_order_release, ...)
而不是 std::memory_order_relaxed - std::memory_order_release
。
所以std::memory_order_acquire - std::memory_order_release
线程只会向堆栈中添加一个新节点,但是当我们在堆栈中没有节点并重置到这个新节点时,它会进入初始状态。
我正在围绕这个主题进行研究,我能找到的最好的就是这篇文章Does exchange or compare_and_exchange reads last value in modification order?
所以问题是,这是真的吗?并且所有RMW函数都会按修改顺序查看最后一个值?无论我们使用什么B
,如果我们使用RMW操作,它将与所有线程(CPU等)同步,并在调用它时找到要写入原子操作的最后一个值?
答案 0 :(得分:0)
因此经过一些研究并询问了一群人,我相信我找到了这个问题的正确答案,我希望这对某人有所帮助。
所以问题是,这是真的吗?所有RMW功能都看到了最后一个 修改顺序中的值?
是的,这是真的。
无论我们使用什么std :: memory_order,如果我们使用RMW操作它 将与所有线程(CPU等)同步并找到最后一个 要被写入原子操作的值被调用?
是的,这也是事实,但有些事情需要突出显示。
RMW操作将同步仅与其一起使用的原子变量。在我们的例子中,它是 head.load
也许您想问为什么我们需要发布 - 如果RMW即使使用宽松的内存顺序进行同步也会获得语义。
答案是因为RMW只适用于它同步的变量,但在RMW之前发生的其他操作可能在另一个线程中看不到。
让我们再次看一下推送功能:
void push(T const& data)
{
counted_node_ptr new_node;
new_node.ptr=new node(data);
new_node.external_count=1;
new_node.ptr->next=head.load(std::memory_order_relaxed)
while(!head.compare_exchange_weak(new_node.ptr->next,new_node, std::memory_order_release, std::memory_order_relaxed));
}
在这个例子中,如果使用两个推送线程,它们在某种程度上不会彼此同步,但可以在这里允许。
两个线程总是会看到最新的头,因为compare_exchange_weak
提供这个。并且新节点将始终添加到堆栈顶部。
但是,如果我们在此行*(new_node.ptr->next)
之后尝试获取此new_node.ptr->next=head.load(std::memory_order_relaxed)
之类的值,则很容易变得丑陋:空指针可能会被取消引用。
这可能是因为处理器可以改变指令的顺序,并且由于线程之间没有同步,第二个线程甚至可以在初始化之前看到指向顶级节点的指针!
这正是发布 - 获取语义提供帮助的地方。它确保在获取部分中可以看到发布操作之前发生的所有操作! 查看并比较书中的清单5.5和5.8。
我还建议您阅读有关处理器如何工作的文章,它可能会提供一些基本信息以便更好地理解。 memory barriers