WPF MultiThreading:这怎么会导致异常? (小心那个调度员!)

时间:2012-06-13 06:23:19

标签: c# wpf multithreading locking dispatcher

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

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 个答案:

答案 0 :(得分: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));
         //...
    });
    //...

答案 1 :(得分:0)

您是否确保所有线程都使用相同的lockObject实例?如果他们不是那么它将不会阻止多个线程到达你的添加代码。

答案 2 :(得分:0)

var key = m.ViewName;语句中移动lock

答案 3 :(得分: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);
}