C# 在等待时调用RaiseCanExecuteChanged会导致死锁
我正在使用来自PRISM的WPF和C# 在等待时调用RaiseCanExecuteChanged会导致死锁,c#,asynchronous,prism,async-await,prism-4,C#,Asynchronous,Prism,Async Await,Prism 4,我正在使用来自PRISM的WPF和DelegateCommand,出现以下问题: 我启动一个异步操作,如: public async void ProgramDevice() { var result = await FirmwareLoader.DownloadFirmwareAsync(); } 在该方法中,将触发一个事件,我已向该事件注册,并应更新我的DelegateCommand,使其无法执行: //UiCommand is of type DelegateCommand En
DelegateCommand
,出现以下问题:
我启动一个异步操作,如:
public async void ProgramDevice()
{
var result = await FirmwareLoader.DownloadFirmwareAsync();
}
在该方法中,将触发一个事件,我已向该事件注册,并应更新我的DelegateCommand
,使其无法执行:
//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged +=
(s, e) => Dispatcher.Invoke(() => UiCommand.RaiseCanExecuteChanged());
现在我遇到了一个问题,raisecancecutechanged
会导致死锁(我选中了,而Dispatcher.Invoke
不会导致死锁,因为当我(例如)显示一个MessageBox时,它可以正常工作)
我是否做错了什么,或者如何解决此问题?发现问题:它不是被触发的
RaiseCanecuteChanged
,而是由它触发的实际CanExecute
。在那里我有一个AsyncLock
,它等待编程任务完成,然后返回我用来描述UiCommand
是否可以执行-->死锁的值,因为编程任务触发了它
我通过简单地使用我需要的值的“sync”属性(它不使用锁,只返回当前值/stat)解决了这个问题。我看到您已经解决了您的问题,但我想我会给出一个更通用的解决方案,帮助您在将来防止此类死锁 在您的情况下,可以通过如下方式轻松避免此死锁:
var result = await FirmwareLoader.DownloadFirmwareAsync().ConfigureAwait(false);
这样做的目的是允许在与原始线程不同的线程上执行延续。这样做并不总是可能的,因为很多时候您需要在UI线程上执行延续,但是对于这个问题,我不认为是这样。因此,基本上,最佳实践是始终使用ConfigureAwait(false)
,除非需要从原始线程恢复执行
详细解释了发生此类死锁的原因以及如何避免它们。另一个推荐阅读是
我是否做错了什么,或者如何解决这个问题
Dispatcher.Invoke
阻塞工作线程,直到UI线程进行所有更新raisecanecutechanged
->CanExecute
方法链)和块Dispatcher.BeginInvoke
异步调用UI线程上的更新
//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged +=
(s, e) => Dispatcher.BeginInvoke(() => UiCommand.RaiseCanExecuteChanged());
这样,当工作线程释放锁定的资源时,UI线程将等待片刻,然后进行更新。但是不会出现死锁。当死锁发生时,您的UI线程会做什么(即它的调用堆栈是什么?@svick:
ProgramDevice
方法按预期在“wait”处等待,事件处理程序进入Invoke
并挂起在raisecancecutechanged
处。调用堆栈似乎很好(下载firmwareasync->OnIsProgrammedChanged->AnonymousMethod).@chrfin:请发布整个调用堆栈。感谢您的输入,但我已经知道了这一点,我正在尽可能使用configurewait(false)
。在这种情况下,由于ProgramDevice
方法在async
调用之后访问UI是不可能的,我在这里省略了该调用以保持代码简短。