Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在等待时调用RaiseCanExecuteChanged会导致死锁_C#_Asynchronous_Prism_Async Await_Prism 4 - Fatal编程技术网

C# 在等待时调用RaiseCanExecuteChanged会导致死锁

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

我正在使用来自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());
现在我遇到了一个问题,
raisecancecutechanged
会导致死锁(我选中了,而
Dispatcher.Invoke
不会导致死锁,因为当我(例如)显示一个MessageBox时,它可以正常工作)

我是否做错了什么,或者如何解决此问题?

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


我通过简单地使用我需要的值的“sync”属性(它不使用锁,只返回当前值/stat)解决了这个问题。

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

在您的情况下,可以通过如下方式轻松避免此死锁:

var result = await FirmwareLoader.DownloadFirmwareAsync().ConfigureAwait(false);
这样做的目的是允许在与原始线程不同的线程上执行延续。这样做并不总是可能的,因为很多时候您需要在UI线程上执行延续,但是对于这个问题,我不认为是这样。因此,基本上,最佳实践是始终使用
ConfigureAwait(false)
,除非需要从原始线程恢复执行

详细解释了发生此类死锁的原因以及如何避免它们。另一个推荐阅读是

我是否做错了什么,或者如何解决这个问题

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

  • UI线程使用工作线程锁定的一些资源(通过上述代码中的
    raisecanecutechanged
    ->
    CanExecute
    方法链)和块

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

  • 确保无死锁的一种可能方法是使用
    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是不可能的,我在这里省略了该调用以保持代码简短。