在我的单身人士中过度空洞检查支持字段?

时间:2014-06-30 11:07:12

标签: vb.net synclock

下面的代码表示我在我的应用程序中使用的单例。让我们假设_MyObject = New Object代表了一个非常昂贵的数据库调用,在任何情况下我都不希望这样做多次。为确保不会发生这种情况,我首先检查_MyObject支持字段是否为空。如果是,我进入SyncLock以确保一次只能有一个线程进入此处。但是,如果在实例化单例之前两个线程超过第一个空检查,则线程B将最终坐在SyncLock上,而线程A将创建实例。在线程A退出锁之后,线程B将进入锁并重新创建实例,这将导致进行昂贵的数据库调用。为了防止这种情况,我在锁中添加了对支持字段的额外空值检查。这样,如果线程B设法在锁定处等待,它将通过并再进行一次空检查,以确保它不会重新创建实例。

那么真的有必要进行两次空检查吗?将摆脱外部空检查并刚刚开始使用Synclock是一样的吗?换句话说,线程锁定变量的速度和让多个线程同时访问后备域一样快吗?如果是这样,外部空检查是多余的。

Private Shared synclocker As New Object
Private Shared _MyObject As Object = Nothing
Public Shared ReadOnly Property MyObject As Object
    Get
        If _MyObject Is Nothing Then 'superfluous null check?
            SyncLock synclocker
                If _MyObject Is Nothing Then _MyObject = New Object
            End SyncLock
        End If

        Return _MyObject
    End Get
End Property

2 个答案:

答案 0 :(得分:2)

这可能会更好地作为答案而不是评论。

因此,使用Lazy实现"只执行一次昂贵的操作,而不是返回对创建的实例的引用":

Private Shared _MyObject As Lazy(Of Object) = New Lazy(Of Object)(AddressOf InitYourObject)

Private Shared Function InitYourObject() As Object
    Return New Object()
End Function

Public Shared ReadOnly Property MyObject As Object
    Get
        Return _MyObject.Value
    End Get
End Property

这是一种非常简单且线程安全的按需一次性初始化方式。 InitYourObject方法处理您需要执行的任何初始化并返回已创建类的实例。在第一次请求时,调用_MyObject.Value时会调用初始化方法,后续请求将返回相同的实例。

答案 1 :(得分:1)

你添加内部If声明是完全正确的(正如你所正确指出的那样,如果没有它,你仍会有竞争条件)。

你也是正确的,从纯逻辑的角度来看,外部检查是多余的。 但是,外部空检查可以避免相对昂贵的SyncLock操作。

考虑一下:如果你已经创建了你的单例,并且你碰巧同时从10个线程中击中了你的属性,那么外部If就是阻止那10个线程排队到什么都没做的东西。同步线程并不便宜,因此添加的If用于提高性能而非功能。