在ASP.NET中使用成员变量进行缓存时出现并发问题

时间:2013-02-28 16:54:42

标签: asp.net vb.net caching concurrency static

我正在使用静态成员来缓存一些数据而我相信我在首次填充缓存数据时遇到某种并发问题。这个问题是零星的,似乎跟随应用程序池的回收(虽然通常不会出现问题)。

最初我的_CachedData成员为null。当调用FindAll()方法时,当_CachedData不为null时,它将使用缓存的数据,否则它将从数据库加载数据。只要对数据执行插入,更新或删除,_CachedData成员就会设置为null,但在应用程序池回收后首次加载应用程序时,这种情况永远不会发生。

当我将一个TestClass对象添加到_CachedData的结果时,我得到的错误是一个对象引用错误。我只是看不出这是怎么可能的,因为我在输入for each循环之前对_CachedData执行了空检查。

Public Class TestClass

    Private Shared _CachedData As Collection(Of TestClass) = Nothing

    Sub New()

    End Sub

    Public Shared Function FindAll() As Collection(Of Content.TestClass)
        Dim result As New Collection(Of TestClass)()

        If _CachedData IsNot Nothing Then
            ' retrieve from cache
            For Each tc As TestClass In _CachedData
                result.Add(CType(tc.MemberwiseClone(), TestClass))
            Next
        Else
            result = GetFromDatabase()

            ' save to cache
            _CachedData = New Collection(Of TestClass)()
            SyncLock _CachedData
                ' make sure the cache wasn't populated while we were aquiring the lock
                If _CachedData.Count = 0 Then
                    For Each tc As TestClass In result
                        _CachedData.Add(CType(tc.MemberwiseClone(), TestClass))
                    Next
                End If
            End SyncLock
        End If

        Return result
    End Function

FAF

1 个答案:

答案 0 :(得分:0)

对于初学者,你实际上不应该锁定对象本身,因为你将对该对象进行更改。您应该更新您的代码:

Private Shared _CachedData As Collection(Of TestClass) = Nothing
Private Shared ReadOnly _CachedDataLock As Object = New Object()

Public Shared Function FindAll() As Collection(Of Content.TestClass)
    Dim result As New Collection(Of TestClass)()

    If _CachedData IsNot Nothing Then
        ' retrieve from cache, using a local instance to avoid race-conditions
        Dim localCachedData As Collection(Of TestClass) = _CachedData
        For Each tc As TestClass In localCachedData 
            result.Add(CType(tc.MemberwiseClone(), TestClass))
        Next
    Else
        result = GetFromDatabase()

        ' save to cache
        SyncLock _CachedDataLock
            ' make sure the cache wasn't populated while we were aquiring the lock
            If _CachedData Is Nothing Then
                _CachedData = New Collection(Of TestClass)()
                For Each tc As TestClass In result
                    _CachedData.Add(CType(tc.MemberwiseClone(), TestClass))
                Next
            End If
        End SyncLock
    End If

    Return result
End Function