RaiseCanExecuteChanged 在等待时调用会导致死锁
本文关键字:死锁 调用 在等待 RaiseCanExecuteChanged | 更新日期: 2023-09-27 18:30:20
我正在使用PRISM的WPF和DelegateCommand
,并遇到以下问题:
我启动一个异步操作,如下所示:
public async void ProgramDevice()
{
var result = await FirmwareLoader.DownloadFirmwareAsync();
}
在此方法中,触发了一个我注册的事件,并且应该更新我的DelegateCommand
以便无法执行:
//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged +=
(s, e) => Dispatcher.Invoke(() => UiCommand.RaiseCanExecuteChanged());
现在我遇到了问题,RaiseCanExecuteChanged
导致死锁(我检查了,Dispatcher.Invoke
不会导致死锁,因为当我例如显示 MessageBox 时,它工作正常)。
我是否做错了什么或如何解决此问题?
我看到你已经解决了你的问题,但我想我会给出一个更通用的解决方案,帮助你防止将来出现这样的死锁。
在您的情况下,您可以使用如下ConfigureAwait
轻松避免此死锁:
var result = await FirmwareLoader.DownloadFirmwareAsync().ConfigureAwait(false);
这样做是允许在与原始线程不同的线程上执行延续。这样做并不总是可能的,因为很多时候您需要在 UI 线程上执行延续,但对于这个问题,我认为情况并非如此。因此,基本上,最佳做法是始终使用ConfigureAwait(false)
,除非您需要从原始线程恢复执行。
本文详细解释了为什么会发生这种死锁以及如何避免它们。另一个推荐阅读是异步编程的最佳实践。
发现问题:
这不是RaiseCanExecuteChanged
,而是由它触发的实际CanExecute
。在那里,我有一个等待编程任务完成的AsyncLock
,然后返回我用来确定是否可以执行UiCommand
的值 ->编程任务触发时的死锁......
我通过使用我需要的值的"sync"属性(不使用锁,只返回当前值/统计信息)简单地解决了它。
我是否做错了什么或如何解决此问题?
-
方法
Dispatcher.Invoke
阻止工作线程,直到 UI 线程进行所有更新 -
UI 线程使用一些被工作线程锁定的资源(通过上面代码中的
RaiseCanExecuteChanged
->CanExecute
方法链)和块 -
死锁,因为工作线程等待 UI 线程完成更新,UI 线程等待工作线程释放锁定的资源
确保没有死锁的一种可能方法是使用 Dispatcher.BeginInvoke
异步调用 UI 线程上的更新。
//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged +=
(s, e) => Dispatcher.BeginInvoke(() => UiCommand.RaiseCanExecuteChanged());
这样,当工作线程释放锁定的资源时,UI 线程将等待片刻,然后进行更新。但不会有僵局。