混合锁和联锁操作是不是安全?

Posted

技术标签:

【中文标题】混合锁和联锁操作是不是安全?【英文标题】:Is it safe to mix locks and interlock operations?混合锁和联锁操作是否安全? 【发布时间】:2010-12-07 23:28:02 【问题描述】:

我有一些代码必须是线程安全的,其行为类似于:

protected long m_RunningValue protected long m_RunningCounter protected object m_Lock = new object(); public long RunningValue get return Interlocked.Read(m_RunningValue); public long RunningCounter get return Interlocked.Read(m_RunningCounter); public void DoCalculation(int newValue, int newQuantity) lock(m_Lock) Interlocked.Add(ref m_RunningValueA, newValue); Interlocked.Add(ref m_RunningCounter, newQuantity); if(Interlocked.Read(ref newQuantity) == 0) ...m_RunningValue gets further modified here

计算必须同时锁定值和计数器,否则竞争条件可能会影响 if(...) 块,但是它们在被读出时根本不需要同步,即如果计数器和值发生变化在尝试阅读两者之间,这对我来说是 100% 好的。

读取时的互锁用于 64 位值的线程安全读取。

    像这样混合联锁和锁是否安全?我在其他网页上读到混合它们是不安全的,但是如果这意味着混合它们是引入微妙错误的好方法,或者如果在系统级别这可能会破坏所涉及的数据结构,我无法找到澄清。

    所有这些互锁(64 位 .NET 4.0 运行时)的成本是否完全违背了在属性 get() 方法周围保存 ReaderWriterSlim 锁的目的?

【问题讨论】:

您无需对newQuantity 进行联锁阅读。关于性能,我猜它取决于读取和更新的相对组合 - 如果它很重要,您可能应该对其进行基准测试而不是猜测。 我看不出混合联锁操作和锁会导致问题的任何原因。 刚刚意识到为什么 Anon 的回答是正确的。如果你在没有Interlock的情况下在锁内进行操作,你需要Interlock读取的属性吗? 【参考方案1】:

关于已编辑的版本,这保证是线程安全的。

考虑 32 位平台。这意味着必须在两个单独的操作中访问 64 位长值。

一个线程很可能读取数字的前半部分,然后从 CPU 中交换出来,而后半部分的值在其背后改变。现在该线程具有完全无效的值。

Interlocked.Read 的文档明确提到:

Read 方法和 Increment、Decrement 和 Add 方法的 64 位重载仅在 System.IntPtr 为 64 位长的系统上才是真正的原子。在其他系统上,这些方法相对于彼此是原子的,但相对于其他访问数据的方式而言不是原子的。因此,要在 32 位系统上实现线程安全,对 64 位值的任何访问都必须通过 Interlocked 类的成员进行。

(强调我的)

编辑:现在它已被重新编辑,这看起来有点傻,但我将把它留在这里以表明如果你在某些地方互锁而不是其他地方,那是没有意义的。

【讨论】:

以上是关于混合锁和联锁操作是不是安全?的主要内容,如果未能解决你的问题,请参考以下文章

WaitForSingleObject 与联锁*

联锁读取 64 位变量

什么是乐观锁和悲观锁?

什么是乐观锁和悲观锁?

订单并发处理--悲观锁和乐观锁任务队列

并发编程锁和队列及生产者消费模型