Interlocked是否提供所有线程的可见性?

Cha*_*pol 7 c# multithreading visibility interlocked

假设我有一个变量"counter",并且有几个线程通过使用Interlocked访问和设置"counter"的值,即:

int value = Interlocked.Increment(ref counter);
Run Code Online (Sandbox Code Playgroud)

int value = Interlocked.Decrement(ref counter);
Run Code Online (Sandbox Code Playgroud)

我可以假设,Interlocked所做的更改将在所有线程中可见吗?

如果没有,我该怎么做才能使所有线程同步变量?

编辑:有人建议我使用volatile.但是当我将"计数器"设置为volatile时,会出现编译器警告"对volatile字段的引用不会被视为volatile".

当我阅读在线帮助时,它说:"通常不应使用ref或out参数传递易失性字段".

Rom*_*kov 6

我可以假设,Interlocked所做的更改将在所有线程中可见吗?

这取决于您如何读取值.如果您"只是"读取它,那么不会,除非您将其标记为易失性,否则这在其他线程中并不总是可见.但这会引起恼人的警告.

作为替代(并且非常优选的IMO),使用另一个Interlocked指令读取它.这将始终在所有线程上看到更新的值:

int readvalue = Interlocked.CompareExchange(ref counter, 0, 0);
Run Code Online (Sandbox Code Playgroud)

返回读取的值,如果为0则将其与0交换.

动机:警告暗示事情不对; 将两种技术(易失性和互锁)结合起来并不是预期的方法.

更新:似乎另一种不使用"volatile"的可靠32位读取方法是使用本答案中的Thread.VolatileRead建议.还有一些证据表明我完全错误地使用32位读取,例如这个Connect问题,尽管我想知道这种区别是否有点迂腐.Interlocked

我的真正含义是:不要将这个答案作为你唯一的来源; 我对此表示怀疑.


min*_*ang 6

x86 CPU上的InterlockedIncrement/Decrement(x86的lock add/dec)会自动创建内存屏障,从而提供对所有线程的可见性(即,所有线程都可以按顺序查看其更新,如顺序内存一致性).内存屏障可以完成所有挂起的内存加载/存储.volatile虽然C#和Java(以及一些C/C++编译器)强制执行volatile内存屏障,但与此问题无关.但是,联锁操作已经由CPU具有内存屏障.

请参阅stackoverflow中的另一个答案.

请注意,我假设C#的InterlockedIncrement/Decrement是x86的lock add/dec的内在映射.

  • 仅硬件可见性不足以暗示"程序"可见性. (2认同)