当我写入/读影响的主存储器?
-
20-09-2019 - |
题
当我写的一个值到一领域,什么保证我得到关于当新的价值将被保存在的主存储器?例如,我怎么知道处理器不要让新的价值在于它是私人的高速缓存,但更新的主存储器?
另一个例子:
int m_foo;
void Read() // executed by thread X (on processor #0)
{
Console.Write(m_foo);
}
void Write() // executed by thread Y (on processor #1)
{
m_foo = 1;
}
有没有可能性之后 Write() 是完成执行,其他一些线执行 Read() 但实际上会看到"0"当前价值?(由于或许是以前写信给m_foo不是脸红了吗?).
什么样的元(旁边的门锁)提供保证写入了冲洗?
编辑
在代码样本我用,编写和阅读被放置在不同的方法。不线。MemoryBarrier仅影响指令的记录,存在于相同的范围?
此外,让我们假设他们将不会通过内联JIT,如何才能确保值写入m_foo不会被存储在一个登记册,但主要的记忆?(或者当m_foo读,它不会得到一个古老的价值从CPU缓冲存储)。
它是可以实现这一目标没有使用锁或'挥发性'的关键字?(此外,我们说我不用原始的类型,而是一个字的中小型结构 [那么易挥发的无法应用].)
解决方案
挥发性和互锁已经提到的,你问元,一个除了列表是您写前使用Thread.MemoryBarrier()
或读取。这保证没有重新排序的内存进行写入和读取。
这是“手动”什么lock
,Interlocked
和volatile
可以自动完成大部分时间做的事情。你可以使用它作为一个完整的更换,以任何其它技术,但它可以说是旅游的最难的路径,所以说MSDN:
“这是难以建立通过使用正确的多线程程序 内存屏障。对于大多数的目的, C#lock语句时,Visual Basic 的SyncLock语句和方法 Monitor类提供更方便, 容易出错的方式不太同步 存储器访问。我们建议您 用它们来代替内存屏障。 “
如何使用内存屏障
一个非常细的例子是VolatileRead
和VolatileWrite
的实现方式中,这两个内部使用MemoryBarrier
。拇指遵循的基本规则是:当你读一个变量,放置内存屏障后的读取的。当写入的值,存储器屏障必须来前写入。
在万一你怀疑这是否是低效率的,然后lock
,考虑到锁定无非是那么的“全防护”,因为它把一个内存屏障的代码块之前和之后(忽略监视器片刻)。这一原则在此出色的权威性文章主题,锁定,挥发性及记忆力障碍阿尔巴哈利。
从反射器:
public static void VolatileWrite(ref byte address, byte value)
{
MemoryBarrier();
address = value;
}
public static byte VolatileRead(ref byte address)
{
byte num = address;
MemoryBarrier();
return num;
}
其他提示
如果你想要的,以确保它写的是迅速和顺序,那么标记 volatile
, 或者(带更多的痛苦)使用 Thread.VolatileRead
/ Thread.VolatileWrite
(不是一个有吸引力的选择,并且容易错过了一个,使它无用的).
volatile int m_foo;
否则你都几乎没有保障的任何东西(只要你交谈的多个线).
你可能还想要看看锁定(Monitor
)或 Interlocked
, 这将达到同样的效果 只要 的 同 方法是使用从所有访问(即所有 lock
, 或者所有 Interlocked
, 等等)。
只要你不使用任何同步,你有没有保证,在一个处理器上运行的线程看到由另一处理器上运行的另一个线程所做的更改。这是因为该值可能在CPU高速缓存中或者一个CPU寄存器进行高速缓存。
因此需要将其标记该变量作为挥发性。读取写入之间,将创造一个“偏偏-Before'-realation。
这不是一个处理器缓存的问题。写操作通常是直通(写去都缓存和主内存)和所有读取将进入高速缓存。但在途中许多其它高速缓存(编程语言,库,操作系统, I / O 缓冲液,等等)。编译器还可以选择保持变量在处理器寄存器和从未其写入主存储器(这是易失性操作被设计用于,避免在寄存器中存储数值时,它可被一个存储器映射I / O)。
如果您有多个进程或多个线程和同步是必须显式地做到这一点的问题,有很多方式,根据不同的使用情况下做的。
有关单线程程序,不关心,编译器会做什么它必须和读取将获得已被写入。