[英]RaiseCanExecuteChanged called while await causes deadlock
I am using WPF and DelegateCommand
from PRISM and have the following problem: 我正在使用WPF和PRISM的
DelegateCommand
并遇到以下问题:
I start an async operation like: 我开始一个异步操作,如:
public async void ProgramDevice()
{
var result = await FirmwareLoader.DownloadFirmwareAsync();
}
Inside this method an event is fired which I registered to and should update my DelegateCommand
so it can't be executed: 在此方法内触发了一个事件,该事件已注册并应更新我的
DelegateCommand
因此无法执行:
//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged +=
(s, e) => Dispatcher.Invoke(() => UiCommand.RaiseCanExecuteChanged());
Now I have the problem, that the RaiseCanExecuteChanged
causes a deadlock (I checked and the Dispatcher.Invoke
does not cause it, because when I eg show a MessageBox instead it works fine). 现在我有一个问题,
RaiseCanExecuteChanged
导致死锁(我检查了并且Dispatcher.Invoke
不会导致死锁,因为当我例如显示一个MessageBox时,它可以正常工作)。
Am I doing something wrong or how can I work around this problem? 我是在做错什么还是该如何解决这个问题?
I see you've already solved your problem, but I thought I'd give a more general solution that will help you prevent such deadlocks in the future. 我看到您已经解决了您的问题,但是我想我会提供一个更通用的解决方案,以帮助您将来防止此类死锁。
In your case, you could easily avoid this deadlock by using ConfigureAwait
like this: 在您的情况下,可以通过使用
ConfigureAwait
这样轻松地避免此死锁:
var result = await FirmwareLoader.DownloadFirmwareAsync().ConfigureAwait(false);
What this does is allows the continuation to be performed on a different thread than the original. 这样做是允许继续执行与原始线程不同的线程。 Doing so is not always possible, since a lot of times you need the continuation to be performed on the UI thread, but for this question I don't believe that's the case.
这样做并非总是可能的,因为很多时候您需要在UI线程上执行继续操作,但是对于这个问题,我认为并非如此。 So basically, the best practice is to always use
ConfigureAwait(false)
unless you need to resume execution from the original thread. 因此,基本上,最佳实践是始终使用
ConfigureAwait(false)
除非您需要从原始线程恢复执行。
This article explains in detail why these kind of deadlocks happen and how to avoid them. 本文详细解释了为什么会发生这种死锁以及如何避免死锁。 Another recommended read is Best Practices in Asynchronous Programming .
另一本推荐的读物是“异步编程最佳实践” 。
Found the problem: 发现问题:
It was not the RaiseCanExecuteChanged
, but the actual CanExecute
which is triggered by it. 不是
RaiseCanExecuteChanged
,而是由它触发的实际CanExecute
。 In there I had an AsyncLock
which waited for the programming task to be finished, before returning the value I use to descide if UiCommand
can be executed --> deadlock as the programming task triggered it... 在这里我有一个
AsyncLock
,它等待编程任务完成,然后返回用于确定是否可以执行UiCommand
的值->死锁,因为编程任务触发了它...
I solved it by simple using the "sync" property (which does not use the lock and just returns the current value/stat) of the value I need. 我通过使用所需值的“ sync”属性(不使用锁,仅返回当前值/ stat)来简单地解决了该问题。
Am I doing something wrong or how can I work around this problem?
我是在做错什么还是该如何解决这个问题?
Method Dispatcher.Invoke
blocks working thread until UI thread makes all updates 方法
Dispatcher.Invoke
阻止工作线程,直到UI线程进行所有更新
UI thread uses some resources locked by working thread (through RaiseCanExecuteChanged
-> CanExecute
method chain in the above code) and blocks UI线程使用了工作线程锁定的一些资源(通过上面的代码中的
RaiseCanExecuteChanged
> CanExecute
方法链)和块
Deadlock since worker thread waits for UI thread to finish update and UI thread waits worker thread to release locked resources 死锁,因为工作线程等待UI线程完成更新,而UI线程等待工作线程释放锁定的资源
A possible way to ensure no deadlocks is to asynchronously invoke updates on UI thread using Dispatcher.BeginInvoke
. 确保没有死锁的一种可能方法是使用
Dispatcher.BeginInvoke
异步调用UI线程上的更新 。
//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged +=
(s, e) => Dispatcher.BeginInvoke(() => UiCommand.RaiseCanExecuteChanged());
This way UI thread will wait for a moment when working thread releases locked resources and then will update. 这样,当工作线程释放锁定的资源然后更新时,UI线程将等待片刻。 But there will be no deadlock.
但是不会有僵局。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.