c# - 围绕多个语句的锁定语句是否确保所有更改对其他线程可见(前提是它们进入相同的互斥体)?

标签 c# synchronization thread-safety locking

如果您在一个锁定代码块中对共享变量进行了多次赋值,这是否一定意味着所有这些更改对其他线程都是立即可见的,一旦它们进入锁定语句就可能在其他处理器上运行在同一个对象上 - 或者没有这样的保证?

很多例子都显示了一个单个“设置”或“获取”一个公共(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 中你用 xy 做了一些事情,你应该返回到使用 lock 关键字。

此外,以下是相同的:

try
{
  x = 10;
  y = 20;
  Thread.MemoryBarrier();
  z = a + 10;
}

关于c# - 围绕多个语句的锁定语句是否确保所有更改对其他线程可见(前提是它们进入相同的互斥体)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11606072/

相关文章:

c# - 大小主窗口

c++ - 如何克服 WaitForMultipleObjects 的 MAXIMUM_WAIT_OBJECTS 限制?

jQuery html 过渡

java - 等待按下按钮(在 GUI 中)以推进代码

objective-c - 快速连续发出多个请求时,RestKit崩溃

C# outlook 按ID、用户名或别名搜索地址antry

c# - 构建环境 CodeAnalysis 任务无法实例化

c# - 如何从 dotnetnuke 获取完整的 url

java - Java中哪些情况需要同步方法访问?

java - 如何终止从 ExecutorService 生成的线程