这是我在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
,因为它会破坏这些测试。
答案 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()
=>死锁。