2011-12-23 27 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不是只读的(它被初始化为一个空列表,这大概意味着它将被应用程序中其他地方的代码修改)。

我的建议是避免共享字段除了不可变的对象,除非你真的了解多线程。

相关问题