Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 写入/读取何时影响主内存?_C#_Multithreading_Lock Free_Memory Model_Memory Fences - Fatal编程技术网

C# 写入/读取何时影响主内存?

C# 写入/读取何时影响主内存?,c#,multithreading,lock-free,memory-model,memory-fences,C#,Multithreading,Lock Free,Memory Model,Memory Fences,当我将一个值写入一个字段时,对于新值何时会保存在主内存中,我能得到什么保证?例如,我如何知道处理器没有将新值保存在其私有缓存中,而是更新了主内存? 另一个例子: 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; }

当我将一个值写入一个字段时,对于新值何时会保存在主内存中,我能得到什么保证?例如,我如何知道处理器没有将新值保存在其私有缓存中,而是更新了主内存?
另一个例子:

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的信可能还没有被刷新过?。
什么样的原语(除了锁)可以确保写入被刷新


编辑

在我使用的代码示例中,写入和读取放在不同的方法中。Thread.MemoryBarrier是否只影响同一作用域中存在的指令重新排序

另外,假设它们不会被JIT内联,如何确保写入m_foo的值不会存储在寄存器中,而是存储在主内存中?(或者在读取m_foo时,它不会从CPU缓存中获取旧值)


是否可以在不使用锁或“volatile”关键字的情况下实现这一点?(另外,假设我没有使用基元类型,而是一个单词大小的结构[因此无法应用volatile]

如果您想确保它被及时有序地写入,那么请将其标记为,或者(更痛苦地)使用/(不是一个有吸引力的选项,并且很容易错过一个,使其无用)

否则,您几乎没有任何保证(只要您谈论多个线程)


您可能还想看看锁定()或,只要对所有访问使用相同的方法(即所有或所有
互锁
,等等),锁定()或将实现相同的效果.

只要不使用任何同步,就不能保证在一个处理器上运行的线程看到在另一个处理器上运行的另一个线程所做的更改。这是因为该值可以缓存在CPU缓存或CPU寄存器中


因此,您需要将变量标记为volatile。这将在读取和写入之间创建一个“之前发生”的关系

这不是处理器缓存问题。写操作通常是通过(写操作同时进入缓存和主内存),所有读操作都将访问缓存。但是还有很多其他的缓存(编程语言、库、操作系统、缓冲区等)。编译器还可以选择将变量保存在处理器寄存器中,并且从不将其写入主存(这就是volatile运算符的设计目的,当它可以是内存映射的I/O时,避免将值存储在寄存器中)

如果您有多个进程或多个线程,并且同步是一个问题,那么您必须明确地进行同步,根据用例的不同,有很多方法可以进行同步


对于单线程程序,不必在意,编译器将执行它必须执行的操作,并且读取将访问已写入的内容。

前面已经提到了Volatile和Interlocked,您要求使用原语,列表中的一个补充是在写入或读取之前使用
Thread.MemoryBarrier()
。这保证了不会对内存写入和读取进行重新排序

这是“手动”完成的
锁定
联锁
易失性
大部分时间都可以自动完成的操作。您可以将其作为任何其他技术的完全替代品,但可以说这是最难走的道路,MSDN是这么说的:

“使用以下方法很难构建正确的多线程程序: 记忆载体。在大多数情况下 C#lock语句,Visual Basic SyncLock语句,以及 Monitor类提供了更简单、更方便的 不易出错的同步方法 内存访问。我们建议您 使用它们而不是内存载体。”

如何使用记忆载体 一个很好的例子是
volatireead
volatirewrite
的实现,它们都在内部使用
MemoryBarrier
。要遵循的基本经验法则是:读取变量时,在读取后放置一个内存屏障。写入值时,内存屏障必须位于写入之前

如果你怀疑这是否效率较低,那么<代码>锁定< /代码>,请考虑锁定不再是“完全击剑”,因为它在代码块之前和之后放置了一个内存屏障(忽略监视器一段时间)。这一原则在本文中得到了很好的解释

从反射器:

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甚至不能真正保证“及时”位,只能保证“有序”。我已经决定我不能正确理解。。。整个内存模型太混乱了。我只会在使用另一个框架(如PFX)时进行无锁编码……关于
volatile
的一个注意事项:当读操作在写操作之后时,这种“顺序”是不保证的,因此,使
volatile
不如人们所期望的那样完美。这似乎是一个鲜为人知的事实,但却是灾难的保证。除此之外,这里还解释了这一点:换句话说,如果您需要在写入之后进行读取,则必须使用完全隔离并使用
联锁的
(使用完全隔离)或传统锁定。@Abel:我只是指写入的顺序。不是“易失性”(通常是内存隔离)只影响指令顺序吗,或者,当值从寄存器“刷新”到主存时,它是否也会产生影响?(或者,强制CPU从主内存而不是其专用缓存获取值)。是否可以告诉处理器不使用“volatile”关键字从主存读取值?(假设我有一个4字节的结构,而不是一个基元类型。然后呢?在我使用的代码示例中,写入和读取放在不同的方法中。Thread.MemoryBarrier是否只影响存在于同一范围内的指令重新排序?另外,假设它们不会被JIT内联,如何确保值
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;
}