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

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

很多例子都显示了一个常见变量的“set”或“get”,并详细介绍了内存障碍,但如果内部有更复杂的语句,会发生什么? 有可能甚至函数调用做其他事情?

像这样的东西:

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

如果此代码在另一个可能在另一个处理器上执行的线程上运行,它是否会对更改的“可见性”做出任何保证?

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

如果答案是否定的 – 也许并解释这些变化何时可见?

锁定块在开始和结束时(块的开始和结束)包括存储器栅栏。 这可确保对内存的任何更改对其他内核(例如,在其他内核上运行的其他线程)可见。 在您的示例中,第一个锁定块中对x,y,z的更改将对任何其他线程可见。 “可见”表示缓存到寄存器中的任何值都将刷新到内存,并且缓存在CPU缓存中的任何内存都将刷新到物理内存。 ECMA 334详细说明锁定块是由Monitor.Enter和Monitor.Exit包围的块。 此外,ECMA 335详细说明Monitor.Enter“将隐式执行易失性读操作……”并且Monitor.Exit“隐式执行易失性写操作。这意味着修改将不会被其他内核/线程看到,直到锁定块的结尾(在Monitor.Exit之后),但如果您对这些变量的所有访问都被锁定保护,则无论如何都不能同时访问不同内核/线程上的所述变量。

这实际上意味着锁定语句保护的任何变量都不需要声明为volatile,以使其修改对其他线程可见。

由于示例代码仅包含依赖于单个共享primefaces操作的操作(对y读取和写入单个值),因此可以获得相同的结果:

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

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

第一个块保证在写入y之前对x的写入是可见的,并且在写入z之前对y的写入是可见的。 它还保证,如果对x或y的写入缓存在CPU缓存中,则在调用VolatileWrite之后,该缓存将立即刷新到物理内存(因此对任何其他线程可见)。

如果在if(y == 10)块中使用xy执行某些操作,则应返回使用lock关键字。

此外,以下内容将完全相同:

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

如果我误解你的问题(非常可能),请原谅我; 但我认为你正在混淆同步可见性的概念。

互斥体(“互斥”)的重点是确保两个代码块不会同时运行。 所以在你的例子中,第一个块:

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

……和第二个街区:

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

…… 永远不会同时执行 。 这就是lock关键字为您保证的。

因此,只要您的代码进入第二个块,变量xyz应处于与第一个块的完全执行一致的状态。 (这假设您访问这些变量的所有sharedObject都以与在这些片段中相同的方式lock sharedObject 。)

这意味着第一个区块内的中间变化的“可见性”与第二个区块的视角无关,因为永远不会有例如x值发生变化而不是yz