假设一个类有一个公共int计数器字段,可以被多个线程访问。这个int值只能自增或自减。

要增加这个字段,应该使用哪种方法,为什么?

锁(this.locker) this.counter + +; 联锁。增量(ref this.counter); 将counter的访问修饰符更改为public volatile。

现在我已经发现了volatile,我已经删除了许多lock语句和Interlocked的使用。但有理由不这么做吗?


当前回答

互锁的函数不锁。它们是原子的,这意味着它们可以在递增过程中不需要上下文切换的情况下完成。因此不存在死锁或等待的机会。

我想说的是,你应该总是更喜欢它,而不是锁和增量。

如果你需要在一个线程中写入,然后在另一个线程中读取,或者你想让优化器不对变量的操作重新排序(因为事情发生在另一个线程中,而优化器不知道),Volatile是有用的。这是一个与增量正交的选择。

如果您想了解更多关于无锁代码的内容,以及编写无锁代码的正确方法,这是一篇非常好的文章

http://www.ddj.com/hpc-high-performance-computing/210604448

其他回答

我做了一些测试来看看这个理论是如何工作的:kennethxu.blogspot.com/2009/05/interlocked-vs-monitor-performance.html。我的测试更侧重于compareexchange,但Increment的结果是类似的。在多cpu环境下,联锁并不需要更快。下面是在2年的16 CPU服务器上对Increment的测试结果。请记住,测试还包括增加后的安全读取,这在现实世界中是典型的。

D:\>InterlockVsMonitor.exe 16
Using 16 threads:
          InterlockAtomic.RunIncrement         (ns):   8355 Average,   8302 Minimal,   8409 Maxmial
    MonitorVolatileAtomic.RunIncrement         (ns):   7077 Average,   6843 Minimal,   7243 Maxmial

D:\>InterlockVsMonitor.exe 4
Using 4 threads:
          InterlockAtomic.RunIncrement         (ns):   4319 Average,   4319 Minimal,   4321 Maxmial
    MonitorVolatileAtomic.RunIncrement         (ns):    933 Average,    802 Minimal,   1018 Maxmial

Lock(…)可以工作,但可能阻塞线程,如果其他代码以不兼容的方式使用相同的锁,则可能导致死锁。

联锁。正确的做法是……更少的开销,因为现代cpu支持这个原语。

Volatile本身是不正确的。试图检索并写回修改后的值的线程仍然可能与执行相同操作的另一个线程发生冲突。

我赞同Jon Skeet的回答,并想为所有想了解“volatile”和“Interlocked”的人添加以下链接:

原子性,波动性和不可变性是不同的,第一部分- (Eric Lippert的神话般的冒险在编码)

原子性、挥发性和不变性是不同的,第二部分

原子性、挥发性和不变性是不同的,第三部分

Sayonara Volatile - (Wayback Machine 2012年乔·达菲的博客快照)

编辑:正如评论中提到的,这些天我很高兴在单个变量的情况下使用Interlocked,这显然是可以的。当它变得更复杂时,我仍然会恢复到锁定…

当你需要增量操作时,使用volatile是没有用的——因为读和写是分开的指令。另一个线程可以在读取后但在回写之前更改值。

就我个人而言,我几乎总是锁定——以一种明显正确的方式获得正确比波动性或interlocking . increment更容易。就我而言,无锁多线程是为真正的线程专家准备的,而我不是。如果Joe Duffy和他的团队构建了很好的库,可以并行化,而不像我构建的那样有太多的锁,那就太棒了,我马上就会使用它——但是当我自己做线程时,我会尽量保持简单。

我只是在这里指出Orion Edwards回答中关于volatile的错误。

他说:

“如果它是不稳定的,这只是确保两个cpu看到相同的数据 同样的时间。”

这是错误的。在微软关于volatile的文档中,提到:

在多处理器系统上,volatile读操作不会 保证获得写入该内存位置的最新值 任何处理器。类似地,volatile写操作不会 保证写入的值对其他人立即可见 处理器。”