2

我正在使用 WPF 和DelegateCommandPRISM 并遇到以下问题:

我开始一个异步操作,如:

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 时它工作正常)。

我做错了什么或者我该如何解决这个问题?

4

3 回答 3

1

我看到您已经解决了您的问题,但我想我会提供一个更通用的解决方案,以帮助您防止将来出现此类死锁。

在您的情况下,您可以通过以下方式轻松避免这种死锁ConfigureAwait

var result = await FirmwareLoader.DownloadFirmwareAsync().ConfigureAwait(false);

这样做是允许继续在与原始线程不同的线程上执行。这样做并不总是可行的,因为很多时候您需要在 UI 线程上执行继续,但对于这个问题,我不相信是这种情况。所以基本上,最好的做法是始终使用ConfigureAwait(false),除非您需要从原始线程恢复执行。

本文详细解释了为什么会发生这种死锁以及如何避免它们。另一个推荐阅读是异步编程的最佳实践

于 2013-09-06T23:11:52.357 回答
0

发现问题:
它不是由它触发的,而是由它触发RaiseCanExecuteChanged的实际问题。CanExecute在那里我有一个AsyncLock等待编程任务完成,然后返回我用来决定是否UiCommand可以执行的值 - >死锁,因为编程任务触发了它......

我通过简单地使用我需要的值的“同步”属性(它不使用锁,只返回当前值/状态)来解决它。

于 2013-09-06T11:24:45.177 回答
0

我做错了什么或者我该如何解决这个问题?

  1. 方法Dispatcher.Invoke阻塞工作线程,直到 UI 线程进行所有更新

  2. UI线程使用了一些被工作线程锁定的资源(通过上面代码中的RaiseCanExecuteChanged->CanExecute方法链)和阻塞

  3. 死锁,因为工作线程等待 UI 线程完成更新并且 UI 线程等待工作线程释放锁定的资源

确保没有死锁的一种可能方法使用Dispatcher.BeginInvoke.

//UiCommand is of type DelegateCommand
Engine.IsProgrammedChanged += 
    (s, e) => Dispatcher.BeginInvoke(() => UiCommand.RaiseCanExecuteChanged());

这样 UI 线程会在工作线程释放锁定的资源时等待片刻,然后更新。但不会出现僵局。

于 2015-02-15T18:26:32.177 回答