我是线程新手,我来自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属性?
答案 0 :(得分:2)
此代码应使用Interlocked.CompareExchange来访问属性getter中的值。将param3( comparand )设置为您知道在变量中看不到的内容,例如Int32.MinValue
,然后该函数只返回_count
的当前值。
如果Interlocked
操作用于对变量的所有访问,则锁是多余的,因为通过Interlocked
类方法的所有访问都是原子的。
答案 1 :(得分:2)
我不知道为什么作者在其他部分使用无锁技术时选择在类的一部分使用锁。但是,我可以假设作者这样做是为了在读取Interger
时创建一个明确的内存屏障。 VB不包含C#的volatile
关键字的等价物,因此只留下4种其他常用方法来使读取安全。我已按照特定方案选择的顺序列出了这些。
需要内存屏障来阻止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()
没有锁定也可以这样做。