围绕是否多个语句lock语句保证所有的变化是显而易见的其他线程(只要它们进入同一个互斥)?(Does

2019-09-20 00:31发布

如果你有一个锁码块内共享变量的任务,它意味着所有这些变化立刻反馈给其他线程可见,在其它处理器上可能运行的,一旦他们对同一个对象进入一个锁声明-或者是有没有这样的保证?

很多的例子在那里显示了一个单一的 “设置”或公共变量的“获取”和进入的内存屏障的细节,但会发生什么,如果一个更复杂的语句集合在里面? 甚至可能函数调用,做其他事情?

事情是这样的:

lock(sharedObject)
{
  x = 10;
  y = 20;
  z = a + 10;
}

如果这个代码在另一个线程,这是另一处理器上执行的可能运行,它作任何保证对变化的“可见度”?

lock (sharedObject)
{
  if (y == 10)
  {
     // Do something. 
  }
}

如果答案是否定的-也许是和这些变化可能成为可见的解释?

Answer 1:

锁定块包括在开始时的存储器围栏和在终点(开始和所述块的端部)。 这确保了对存储器的任何变化都将其他核(在其它核心上运行例如其他线程)可见。 在你的榜样,改变X,Y,Z在你的第一个锁块将是任何其他线程可见。 “可见”是指缓存到一个寄存器中的任何值将被刷新到存储器中,并且在CPU中的缓存中的任何存储器将被刷新到物理存储器。 ECMA 334点的细节,锁块是由Monitor.Enter和Monitor.Exit包围的块。 此外,ECMA 335细节Monitor.Enter“应隐式执行挥发性读操作......”和Monitor.Exit“隐式执行挥发性的写操作,这是否意味着修改不会对其他核心/线程,直到可见锁块的一端(Monitor.Exit后),但如果所有的这些变量的访问被锁保护,就不可能有同时访问说,在不同的核心/线程变量反正。

这实际上意味着,由lock语句把守任何变量不需要被声明为volatile,以便有其修改其他线程可见。

由于示例代码只包含依赖于一个单一的共享原子操作的操作(读取和单个值的y写),你可以得到相同的结果有:

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

if(y == 10)
{
// ...
}

第一块保证写入到x是写入到y和写入到y之前可见是写入到z之前是可见的。 这也保证,如果写入x或y在CPU中的高速缓存中高速缓存的高速缓存将调用VolatileWrite后立即刷新到物理内存(从而对任何其他线程可见)。

如果内if(y == 10)阻止你做的东西xy ,你应该恢复使用lock的关键字。

此外,以下将是相同的:

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


Answer 2:

原谅我,如果我误解你的问题(很可能); 但我认为你在迷茫混合的同步可见性的概念操作。

互斥(“互斥”)的整点是要确保两个代码块将不能同时运行。 所以,在你的榜样,第一块:

lock(sharedObject)
{
  x = 10;
  y = 20;
  z = a + 10;
}

...和第二块:

lock (sharedObject)
{
  if (y == 10)
  {
     // Do something. 
  }
}

将永远不会在同一时间执行 。 这就是lock关键字保证你。

因此, 任何时候你的代码已经进入了第二块,变量xy ,和z应该是在与第一的完整执行一致的状态。 (这是假设你到处访问这些变量,你locksharedObject在你的这些片段具有相同的方式。)

这意味着,所述的第一个块内的中间变化“能见度”是从第二的角度无关,因为绝不会有时,例如,在改变为值的时间x已经发生但不yz



文章来源: Does a lock statement around multiple statements ensure that all the changes are visible to other threads (provided they enter the same mutex)?