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
好吧,
在您发布的代码中,我没有看到任何数据竞赛。
如果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);
}