如果您在一个锁定代码块中对共享变量进行了多次赋值,这是否一定意味着所有这些更改对其他线程都是立即可见的,一旦它们进入锁定语句就可能在其他处理器上运行在同一个对象上 - 或者没有这样的保证?
很多例子都显示了一个单个“设置”或“获取”一个公共(public)变量,并详细介绍了内存屏障,但是如果里面有一组更复杂的语句会发生什么?甚至可能执行其他操作的函数调用?
像这样:
lock(sharedObject)
{
x = 10;
y = 20;
z = a + 10;
}
如果此代码在另一个线程上运行,而该线程可能在另一个处理器上执行,它是否对更改的“可见性”做出任何保证?
lock (sharedObject)
{
if (y == 10)
{
// Do something.
}
}
如果答案是否 - 也许并解释何时这些变化可能变得可见?
最佳答案
锁 block 在开始和结束( block 的开始和结束)处包括一个内存栅栏。这确保对内存的任何更改对其他内核可见(例如,在其他内核上运行的其他线程)。在您的示例中,任何其他线程都可以看到第一个锁 block 中对 x、y、z 的更改。 “可见”意味着缓存到寄存器中的任何值都将被刷新到内存中,而缓存在 CPU 缓存中的任何内存都将被刷新到物理内存中。 ECMA 334 详细说明锁 block 是被 Monitor.Enter 和 Monitor.Exit 包围的 block 。此外,ECMA 335 详细说明 Monitor.Enter“应隐式执行 volatile 读取操作......”和 Monitor.Exit“隐式执行 volatile 写入操作。这确实意味着修改对其他核心/线程不可见,直到锁 block 的末尾(在 Monitor.Exit 之后),但是如果您对这些变量的所有访问都受到锁的保护,那么无论如何都不能同时访问不同内核/线程中的所述变量。
这实际上意味着任何由 lock 语句保护的变量都不需要声明为 volatile 以使它们的修改对其他线程可见。
由于示例代码只包含依赖于单个共享原子操作的操作(读取和写入单个值到 y),您可以通过以下方式获得相同的结果:
try
{
x = 10;
y = 20;
Thread.VolatileWrite(ref z, a + 10);
}
和
if(y == 10)
{
// ...
}
第一个 block 保证对 x 的写入在对 y 的写入之前可见,对 y 的写入在对 z 的写入之前可见。它还保证,如果对 x 或 y 的写入被缓存在 CPU 缓存中,那么在调用 VolatileWrite 之后,该缓存将立即刷新到物理内存(因此对 任何 其他线程可见)。
如果在 if(y == 10)
block 中你用 x
和 y
做了一些事情,你应该返回到使用 lock
关键字。
此外,以下是相同的:
try
{
x = 10;
y = 20;
Thread.MemoryBarrier();
z = a + 10;
}
关于c# - 围绕多个语句的锁定语句是否确保所有更改对其他线程可见(前提是它们进入相同的互斥体)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11606072/