.net 为什么需要锁来实现只读int属性?
我是线程新手,我在一个应用程序中访问了一个自定义线程池实现示例。我只是粘贴代码的必要部分:.net 为什么需要锁来实现只读int属性?,.net,multithreading,locking,.net,Multithreading,Locking,我是线程新手,我在一个应用程序中访问了一个自定义线程池实现示例。我只是粘贴代码的必要部分: Public Class ThreadPool Private CountLock As New Object Private _Count As Integer Public ReadOnly Property ThreadCount() As Integer Get SyncLock CountLock Return _C
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属性?这没有意义,因为在修改属性时没有锁。可能代码以前没有使用联锁操作,甚至在打开/关闭时也在使用SyncLock?在这种情况下,读取访问中也确实需要SyncLock。此代码应用于访问属性getter中的值。将param3(comparand)设置为您知道在变量中看不到的值,如
Int32.MinValue
,然后函数只返回\u count
的当前值
如果
Interlocked
操作用于变量的所有访问,那么锁是多余的,因为通过Interlocked
类方法进行的所有访问都是原子的。我不知道作者为什么选择在类的一部分使用锁,而在其他部分使用无锁技术。但是,我可以假设作者这样做是为了在读取integer
时创建一个显式的内存障碍。VB不包含与C#的volatile
关键字等价的内容,因此只剩下4种常用的方法来确保读取安全。我已经按照我为这个特定场景选择的顺序列出了这些
- 联锁比较交换
- 螺纹
- Thread.MemoryBarrier
- 同步块
Sub LoggingThread()
Do While True
Trace.WriteLine(ThreadPool.ThreadCount)
Loop
End Sub
在本例中,CLR可能会内联ThreadCount
,然后潜在地“提升”对\u Count
的读取,并在循环开始之前将其缓存在CPU寄存器中。其效果是始终显示相同的值。1
1实际上,
Trace.WriteLine
调用本身会生成一个内存屏障,这会导致代码意外安全。该示例旨在简单说明可能发生的情况。锁将强制设置内存屏障,因此,如果上次写入的值是由其他CPU写入的,则不会读取CPU缓存中的过时值。使用Thread.volatireRead()
也可以实现同样的效果,而不需要锁定。您确定这没有意义吗?添加锁会增加内存障碍,这将阻止指令重新排序,这可能会有所不同。是否阻止对ThreadCount之类的属性进行指令排序?我不这么认为-它将纯粹是一个信息/调试属性。任何真正的逻辑都可以用_Count来完成。@Lasse对int
的单独读取在.NET中是原子的。只有当调用者同时调用其他成员并且有足够的内联时,重新排序才会出现问题:可能是,但可能意味着紧密耦合,这可能意味着锁定应该在调用者中。可能是,我想指出的是,这个问题可能不像所指出的那样明确。我当然不知道答案。@Richard,重新排序不会有问题,但CPU缓存会有问题。链接中的代码甚至不像线程池。我想最后我会将此页面的链接发送给博客作者:)我想我在学习锁时错过了一些东西。你能解释一下,如果他没有使用Interlocked,而只是在Open/Close中使用锁,为什么锁是必需的吗?@davsan-Interlocked类使用平台特定的处理器指令来保证在访问原始类型的变量(如Int32
和Int64
)或本机指针时的原子行为。如果您可以确保所有对\u count
的访问都是通过联锁的
,这将是处理\u count
上线程安全的最有效方法。任何更高级别的锁都会更昂贵。否则,请摆脱互锁
用法,并在任何地方使用锁(\u count)
混合搭配都没有意义。@Steve-好的,我明白你的解释了。我想我错过的是:afaik,lock(CountLock){return\u count;}限制一次只能有一个线程执行{return\u count}部分,那么如果多个线程同时执行同一部分,那么缺点是什么呢?我想我不知道阅读安全问题?@davsan-只要不在其他任何地方使用_count,这可能会起作用。但是,我不确定将联锁操作(在机器级别锁定)与非联锁操作(例如,使用托管代码锁()进行保护)混合使用有多可靠。当另一个线程通过Interlocked.Decrement
或Interlocked.Increment
更新底层内存时,如果您碰巧进入托管代码锁(),我不确定是否保证原子读取。除非您确定,否则我不会混合使用这些数据保护类型。@davsan-另请参阅我关于此主题的问题: