Winforms调用异步方法挂起程序
我一直在解决这个问题,但现在我真的想了解出了什么问题。 我有一个相当简单的应用程序(它是youtrack的turtoise SVN插件,但我可以用一个简单的winforms应用程序重现这个问题)。
我有一个异步方法ResolveIssue
public async Task ResolveIssue(Issue issue, int revision, string[] pathList) { await Task.Delay(1000); return true; }
我需要做的就是创建一个死锁,在Button
事件处理程序中调用这个异步方法,并调用Task.Wait
或Task.Result
,就像这样
private void buttonOk_Click(object sender, System.EventArgs e) { var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList); if (asyncResolvedIssue.Result) {} // <== deadlock! }
现在我明白拥有异步方法并主动等待它是相当奇怪的,但为什么会产生死锁?!
您的问题是因为您在调用Task.Delay
时阻止了UI线程,并告诉在Task.Delay
之后在UI线程上运行的延续。 因此,您正在阻止UI等待在等待UI变为空闲时被阻止的任务,这是一个典型的死锁。
两种解决方案 首先使按钮单击异步。
private async void buttonOk_Click(object sender, System.EventArgs e) { var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList); if (await asyncResolvedIssue) {} // <== no deadlock! }
事件处理程序是唯一允许您执行async void
。
另一个选项是告诉Task.Delay
它不需要通过将ConfigureAwait(bool)
设置为false来在UI线程上运行其余的function。
public async Task ResolveIssue(Issue issue, int revision, string[] pathList) { await Task.Delay(1000).ConfigureAwait(false); return true; }
现在, Task.Delay
之后的代码Task.Delay
在线程Task.Delay
线程而不是UI线程上运行,并且不会被UI线程当前被阻止的事实阻止。