Volatile.Write新鲜保证

问题描述 投票:2回答:2

Volatile.Write的文档说明如下:

将指定的对象引用写入指定的字段。在需要它的系统上,插入一个内存屏障,阻止处理器重新排序内存操作,如下所示:如果在代码中此方法之前出现读取或写入,则处理器无法在此方法之后移动它。

价值T 写入的对象引用。立即写入引用,以便计算机中的所有处理器都可以看到它。

但似乎引用1和2是矛盾的。

对于第二个引用是真的,我认为第一个引用必须改变如下:

如果出现读或写 之前 在代码中使用此方法后,处理器无法移动它 后 在此方法之前。

Volatile.Write实际上是否意味着其他线程可以保证及时获取写入,或者第二个引用是误导?

在我看来,好像所有这些“易失性”/“内存障碍”似乎都集中在确保如果写入暴露给其他线程,它们以正确的顺序暴露,但我似乎无法找到实际会发生什么强迫他们暴露。

我知道可能很难/不可能立即向其他线程公开写入,但是如果没有易失性写入/读取,则存在从不暴露写入的情况。所以似乎必须有一种方法来确保写入“最终”,但我不确定那是什么。写入是否始终在.NET中公开但读取是否可以缓存?如果是这样,Volatile.Read会停止这种缓存行为吗?

(注意我已经阅读了Joseph Albahari's Threading in C#,它倾向于建议我在读取之前和写入之后需要明确的内存屏障,尽管不清楚为什么即使这样也应该有效,因为Thread.MemoryBarrier的文档似乎没有明确说明写入是显示给其他线程)。

c# multithreading memory-barriers
2个回答
3
投票

你有点误解障碍的概念。正如你写的那样

写入的对象引用。立即写入引用,以便计算机中的所有处理器都可以看到它。

所以这里真正重要的单位是处理器,而不是线程。

因此,涉及处理器,处理器缓存,存储缓冲区和失效队列。 当处理器将某些东西写入内存时,它看起来像那个或类似于enter image description here

主题处于商店缓冲水平。正如您所看到的,当您编写或读取时会发生很多事情,并且系统中的所有处理器都不会立即发生这种情况。开始时,读或写命令放置在处理器的存储缓冲器中,并且这些命令可以被重新排序,换句话说,由处理器以不同的顺序执行。

当发生这种情况时,如果操作是写入并且当前工作的处理器不知道其他处理器所做的更改,则其他处理器不知道更改。

放置屏障时,这意味着应该在执行任何读取或写入之前完成存储缓冲区或失效队列中的操作。这对于跨处理器实现CPU缓存是必要的。因此,基本上没有机制来跨线程同步任何数据,我们正在跨处理器同步数据。

当线程A在处理器1上写入内容并且线程B在处理器1上读取某些内容时,它们都首先查看存储缓冲区,因此它们读取实际数据,无论是否放置任何障碍。

这只是所涉及的机制的概述,也许我在某些细节上是错的。如果你读到关于MESI protocolthis PDF with explanation on invalidation queues and store buffers的信息,你可以找到完整的信息


2
投票

我同意你的观点,MSDN文档中的描述有点令人困惑。我会说“立即”在这里以及与并行过程相关的任何主题都是强有力的。结果将不会立即显示,但文档没有说明 - 它表示将立即写入值,即只要所有先前的加载/存储操作结果全局可见,写入值的存储操作将是立即发起。

至于内存障碍,它们只能保证先前的操作暴露(全局可见性),因为本质上内存屏障是CPU遇到的指令使CPU“等待”使所有挂起的加载/存储操作全局可见而Volatile.Write所写的全球价值观的时刻既不是障碍也不是Volatile.Write的关注。

现在关于在无锁编程中使用屏障的建议。当然,这是有道理的,因为它确保了全局可见性的顺序,这对于多核系统来说是实际的。当您无法确定事件B总是在事件A之后发生时,您无法构建应该在多核环境中执行的可靠逻辑。

© www.soinside.com 2019 - 2024. All rights reserved.