0

我在消息处理程序中有以下代码(可以在任何线程上调用):

private readonly Dictionary<string,IView> _openedViews = new Dictionary<string,IView>();
private readonly object _lockObject = new object();

public MainView() 
{
    Messenger.Default.Register<ViewChangeMessage>(this, "adminView", m =>
    {
         var key = m.ViewName;
         lock (_lockObject)
         {
            if (_openedViews.ContainsKey(key) == false)
                _openedViews.Add(key, GetView(key));
           content.Content = _openedViews[key];
         }
         //...
    });
    //...

我怎样才能得到这个异常:An element with the same key already exists in the System.Collections.Generic.Dictionary<TKey,TValue>.

如果我快速导致消息被多次发送,则会产生异常。

编辑:为代码添加了更多上下文,Messenger来自 Galasoft.MVVMLight

4

4 回答 4

1

好吧,在您发布的代码中,我没有看到任何数据竞争。

如果GetView不能导致数据竞争,您可以尝试用ConcurrentDictionary.GetOrAdd替换整个锁定代码块:

private readonly ConcurrentDictionary<string,IView> _openedViews = 
          new ConcurrentDictionary<string,IView>();

public MainView() 
{
    Messenger.Default.Register<ViewChangeMessage>(this, "adminView", m =>
    {
         var key = m.ViewName;
         content.Content = _openedViews.GetOrAdd(key, GetView(key));
         //...
    });
    //...
于 2012-06-13T07:43:19.103 回答
0

您是否确定所有线程都使用同一个 lockObject 实例?如果不是,那么它不会阻止多个线程访问您的添加代码。

于 2012-06-13T07:11:56.320 回答
0

移动var key = m.ViewName;内部lock声明。

于 2012-06-13T08:59:13.420 回答
0

这是发生了什么:GetView 实例化了一个视图,该视图在某处有一个长时间运行的操作(在后台线程上等待),因此等待不会锁定 UI 有人介绍了这段代码:

public static void DoEvents()
{
    DispatcherFrame frame = new DispatcherFrame();
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
        new DispatcherOperationCallback(ExitFrame), frame);
    Dispatcher.PushFrame(frame);
}

并且由于 PushFrame,第二条消息在与第一条相同的线程上处理,因此锁没有阻止它。
一旦我将代码重新排列为此,问题就消失了:

if (_openedViews.ContainsKey(key) == false)
{
   _openedViews.Add(key, null);
   _openedViews[key] = ServiceRegistry.GetService<IShellService>().GetView(key);
}
于 2012-06-13T13:19:08.970 回答