WPF 多线程处理:这怎么还会导致异常?(小心那个调度员!

本文关键字:调度员 异常 多线程处理 WPF | 更新日期: 2023-09-27 18:36:55

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

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

WPF 多线程处理:这怎么还会导致异常?(小心那个调度员!

好吧,

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

如果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));
         //...
    });
    //...
您是否确保

所有线程都使用相同的 lockObject 实例?如果不是,则不会阻止多个线程进入您的添加代码。

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

以下是发生的事情: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);
}