为什么需要锁来实现readonly int属性?

时间:2010-10-04 08:33:03

标签: .net multithreading locking

我是线程新手,我来自blog中的自定义线程池实现示例。我只是粘贴代码的必要部分:

Public Class ThreadPool

    Private CountLock As New Object
    Private _Count As Integer

    Public ReadOnly Property ThreadCount() As Integer
      Get
         SyncLock CountLock
           Return _Count
         End SyncLock
      End Get
    End Property

    Public Sub Open()
      Interlocked.Increment(_Count)
    End Sub

    Public Sub Close()
      Interlocked.Decrement(_Count)
      ....
    End Sub

EndClass

我的问题是,为什么我们需要一个锁来实现readonly ThreadCount属性?

4 个答案:

答案 0 :(得分:2)

此代码应使用Interlocked.CompareExchange来访问属性getter中的值。将param3( comparand )设置为您知道在变量中看不到的内容,例如Int32.MinValue,然后该函数只返回_count的当前值。

如果Interlocked操作用于对变量的所有访问,则锁是多余的,因为通过Interlocked类方法的所有访问都是原子的。

答案 1 :(得分:2)

我不知道为什么作者在其他部分使用无锁技术时选择在类的一部分使用锁。但是,我可以假设作者这样做是为了在读取Interger时创建一个明确的内存屏障。 VB不包含C#的volatile关键字的等价物,因此只留下4种其他常用方法来使读取安全。我已按照特定方案选择的顺序列出了这些。

  • Interlocked.CompareExchange
  • Thread.VolatileRead
  • Thread.MemoryBarrier
  • 的SyncLock

需要内存屏障来阻止VB或JIT编译器移动指令。在没有存储器屏障的情况下最可能的优化是将读取提升到循环之外。考虑ThreadCount属性的实际使用。

Sub LoggingThread()
  Do While True
    Trace.WriteLine(ThreadPool.ThreadCount)
  Loop
End Sub

在此示例中,CLR可能内联ThreadCount,然后可能“提升”_Count的读取并在循环开始之前将其缓存在CPU寄存器中。效果将是始终显示相同的值。 1

1 实际上,Trace.WriteLine调用本身会产生一个内存屏障,导致代码偶然安全。该示例旨在简单说明可能发生的情况。

答案 2 :(得分:1)

没有意义,因为没有锁定你修改属性的地方。也许代码之前没有使用Interlocked操作,甚至在Open / Close中使用SyncLock?在这种情况下,同样在读取访问中也需要SyncLock。

答案 3 :(得分:1)

锁将强制执行内存屏障,因此如果写入的最后一个值是由其他CPU写入的,则不会读取CPU缓存中的过时值。 Thread.VolatileRead()没有锁定也可以这样做。