Prism异步场景下通过事件关闭Dialog时报跨线程访问对象异常
问题根因
你遇到的跨线程访问异常核心原因是:
ContinueWith默认会在线程池的后台线程执行后续逻辑,导致LoadingFinishedEvent的发布操作不在UI线程运行- 订阅事件时未指定调度线程,
CloseDialog方法也在后台线程执行,而对话框属于UI元素,所有UI相关操作必须在UI线程执行,因此触发异常
解决方案
方案1:修改事件订阅的线程调度(最推荐)
Prism的PubSubEvent订阅时支持指定线程执行选项,直接要求事件回调在UI线程执行即可,修改LoadingDialogViewModel的构造函数:
public LoadingDialogViewModel(IEventAggregator eventAggregator) { // 增加ThreadOption.UIThread参数,指定事件处理逻辑在UI线程运行 eventAggregator.GetEvent<LoadingFinishedEvent>().Subscribe(close => CloseDialog(null), ThreadOption.UIThread); }
方案2:优化异步逻辑,避免后台线程发布事件
你当前使用的ContinueWith写法冗余且容易吞掉任务异常,直接用await会自动切回原同步上下文(UI线程)执行后续代码,修改WarehouseViewModel代码:
private async void LoadData() { await WarehouseListLoad(); // await后默认回到UI线程,此处发布事件天然线程安全 _eventAggregator.GetEvent<LoadingFinishedEvent>().Publish(true); } private async Task WarehouseListLoad() { _dialogService.ShowDialog("LoadingDialogView"); List<Warehouse> warehouses = await Task.Run(() => { return _warehouseStore.GetAll(); }); // 此处可直接给绑定属性赋值,无需担心跨线程问题 }
兜底方案:手动调度UI线程执行关闭逻辑
如果场景特殊不确定执行线程,可以在RaiseRequestClose中主动调度到UI线程执行操作,WPF环境下写法如下:
public virtual void RaiseRequestClose(IDialogResult dialogResult) { if (Application.Current.Dispatcher.CheckAccess()) { RequestClose?.Invoke(dialogResult); } else { Application.Current.Dispatcher.Invoke(() => RequestClose?.Invoke(dialogResult)); } }
以上方案任选其一即可解决问题,推荐方案1和方案2搭配使用,写法更规范也能避免后续其他异步UI问题。
内容的提问来源于stack exchange,提问作者horst
相关产品推荐
相关产品推荐

