为什么在WPF中使用ManualResetEvent和异步方法时会发生死锁?

时间:2017-09-13 14:06:40

标签: c# wpf asynchronous mutex

这是我在WPF.NET中遇到的一个问题。为了说明这个问题,让我们来看看下面的课程:

public class TaskRunnerWithProgressFeedback(){
     ManualResetEvent _event = new ManualResetEvent(false);
     public void RunTask(Action action) {
        _event.Reset();
        //Display loading screen
        RunAsync(action);
        Console.WriteLine("Load completed.");
        //Hide loading screen
        _event.WaitOne();
    }

    private async void RunAsync(Action action) {
        await Task.Run(() => action.Invoke());
        _event.Set();
    }
} 

所以我在这里有这个类,我将从UI线程调用RunTask方法。 例如:

private void Button1_OnClick(object sender , RoutedEventArgs e) {
    var x = new TaskRunnerWithProgressFeedback();
    x.RunTask(()=>{ /*Some time-consuming action*/ });
}

单击Button1时,整个程序会陷入僵局。你对这种情况有什么解释吗?

脚注:我需要TaskRunnerWithProgressFeedback课程才能进行行为测试。我没有使用BackgroundWorker,因为它会破坏这些测试。

1 个答案:

答案 0 :(得分:2)

_event.Set()方法中创建的Task完成后,应该在UI线程上执行对RunAsync的调用。

如果在该任务完成之前在UI线程上调用_event.WaitOne(),则会发生死锁。

因为UI线程等待设置ManualResetEvent,但它永远不会,因为调用Set()方法的代码无法在UI线程上执行,因为它被{WaitOne()阻塞{1}}致电。

这基本上是async / await的工作原理。 async方法同步运行,直到它命中await,然后返回给调用者。捕获上下文(在这种情况下为调度程序线程),然后在等待方法具有调用async方法的同一调度程序/ UI线程上执行async方法的其余部分。完成。

但是当上下文线程空闲时,该方法的其余部分当然不能执行。在这种情况下,它永远不会是因为它等待async方法的其余部分来调用Set() =>死锁。