2011-12-23 77 views
1
Public Enum ERight 
    ECanInvite = 0 
    ECanCreate = 1 
    ECanDelete = 2 
    etc... 
    End Enum 


    Public Enum EUserType 
    EAdministrator = 0 
    EPartner_level1 = 1 
    EPartner_level2 = 2 
    ENormalUser = 3 
    ...etc 
    End Enum 

這下面子,就rights.add一個錯誤,有時會拋出這個錯誤:An item with the same key has already been added.我的代碼拋出,我認爲應該是不可能

這怎麼可能?

Private Shared rights As Dictionary(Of ws_garuda.EUserType, List(Of ERight)) = Nothing 

Private Sub initRoles() 
    rights = New Dictionary(Of EUserType, List(Of ERight)) 
    rights.Clear() 
    ' Set all rights to false for all roles 
    For Each usertype As EUserType In DirectCast([Enum].GetValues(GetType(EUserType)), EUserType()) 
     rights.Add(usertype, New List(Of ERight)) 
    Next 
End sub 
+2

您是否在多線程應用程序(例如ASP.NET)中?您不顯示在多個線程之間共享的「權限」字段/屬性?你所有的枚舉值是獨一無二的嗎? – Joe 2011-12-23 11:53:26

+0

@Joe是, 權利被定義爲: 公共類CRights 私人共享權利詞典(ws_garuda.EUserType,列表(中ERight))=無 所有枚舉值是唯一 – Muleskinner 2011-12-23 11:57:25

+0

請更改標題的東西,說得通。顯然這不是不可能的,因爲它正在發生。 – 2011-12-23 11:57:49

回答

0

的錯誤發生,因爲你是在多線程環境中編寫一個Shared對象。

解決方法是增加一個鎖(在這種情況下,呼叫到Clear()變得多餘):

Private Shared rights As Dictionary(Of ws_garuda.EUserType, List(Of ERight)) = Nothing 
Private Shared rightsLock As Object = New Object() 

Private Sub initRoles() 
    SyncLock rightsLock 
    rights = New Dictionary(Of EUserType, List(Of ERight)) 
    ' Set all rights to false for all roles 
    For Each usertype As EUserType In DirectCast([Enum].GetValues(GetType(EUserType)), EUserType()) 
     rights.Add(usertype, New List(Of ERight)) 
    Next 
    End SyncLock 
End sub 
+0

此修復程序不足,請參閱我的答案。 – Joe 2011-12-24 09:43:35

+0

根據提供的代碼,此修補程序已足夠。閱讀詞典時也需要鎖定。 – RickNZ 2011-12-25 02:12:19

0

有沒有什麼能夠阻止你的代碼在不同的地方同時調用initRoles?

因爲它是一個共享變量有可能一個電話可以得到,清除變量,然後另一個呼叫可以進來,清除變量,然後他們都開始增加值相同的參考。取決於如何在內部處理所有阻塞。

讓我想起Dijkstra's Semaphores :)這讓我回想起來。

無論如何,我可以建議將這個塊包裝在某個東西中,以表明它是關鍵代碼,並且應該一次運行一次嗎?我在VB中有點生疏,但SyncLock聲明看起來像是票據(來自MSDN鏈接中的代碼和示例)。

0

initRoles是一種實例方法,而rights是一個共享/靜態字典。 WebApplication中的共享意味着它在所有http請求之間共享,因爲它們是不同的線程。 initRoles從不同的請求被調用,並且所有更改rights在同一時間有什麼可能發生。

你沒有顯示你在哪裏打電話initRoles但我確定它是從HttpContext調用的。

如果你想在字典中共享,你也應該initRoles共享。 例如,您可以使用CRights類的共享構造函數來調用它:

Shared Sub New() 
    initRoles() 
End Sub 

Private Shared Sub initRoles() 
    rights = New Dictionary(Of EUserType, List(Of ERight)) 
    ' blah... ' 
End sub 

這種方法需要的字典在應用程序生命週期可能被期望或沒有初始化一次。您也可以公開該方法以啓用重新初始化它。從您的用戶/角色管理。

0

通過@RickNZ提出的解決方法是不夠的。 Dictionary類是不是線程安全的,任何同步使其使用線程安全需要涉及的讀者,以及作家。 .NET 4.0有一個ConcurrentDictionary類,和there exist implementations for .NET 2.0

避免需要線程安全的Dictionary類的最直接的問題最簡單的解決方案是在將共享字典分配給共享的rights字段之前完全構建共享字典。最簡單的方法是改變InitRoles到一個共享的函數,返回一個字典

Private Shared rights As Dictionary(Of ws_garuda.EUserType, List(Of ERight)) = InitRoles() 

Private Shared Function InitRoles() As Dictionary(Of ws_garuda.EUserType, List(Of ERight)) 
    Dim tempRights As Dictionary(Of ws_garuda.EUserType, List(Of ERight)) 
    tempRights = New Dictionary(Of EUserType, List(Of ERight)) 
    ' Set all rights to false for all roles 
    For Each usertype As EUserType In DirectCast([Enum].GetValues(GetType(EUserType)), EUserType()) 
     tempRights.Add(usertype, New List(Of ERight)) 
    Next 
    InitRoles = tempRights 
End Function 

然而,即使這樣也不能使你的代碼是線程安全的。任何訪問字典中的List(Of ERight)值也需要同步。而且你的代碼意味着這個List不是隻讀的(它被初始化爲一個空列表,這大概意味着它將被應用程序中其他地方的代碼修改)。

我的建議是避免共享字段除了不可變的對象,除非你真的瞭解多線程。

相關問題