Winforms 调用异步方法挂断程序 [英] Winforms call to async method hangs up program
问题描述
我解决这个问题已经有一段时间了,但现在我真的很想知道哪里出了问题.我有一个相当简单的应用程序(它是一个用于 youtrack 的 turtoise SVN 插件,但我可以用一个简单的 winforms 应用程序重现这个问题).
I have been working around this problem for a while, but now I would really like to understand what goes wrong. I have a rather simple application (it's a turtoise SVN plugin for youtrack, but I can reproduce the problem with a trivial winforms app).
我有一个异步方法 ResolveIssue
public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
await Task.Delay(1000);
return true;
}
创建死锁所需要做的就是在 Button
事件处理程序中调用这个异步方法,然后调用 Task.Wait
或 Task.Result
,像这样
All I have to do to create a deadlock is call this async method in a Button
event handler, and call Task.Wait
or Task.Result
, like this
private void buttonOk_Click(object sender, System.EventArgs e)
{
var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
if (asyncResolvedIssue.Result) {} // <== deadlock!
}
现在我明白有一个异步方法并主动等待它很奇怪,但为什么会产生死锁?!
Now I understand it's rather weird to have an async method and actively wait for it, but why would it generate a deadlock?!
推荐答案
您的问题是因为您在调用 .Result
时阻塞了 UI 线程,并且您在 Task 之后告诉了延续.延迟
在 UI 线程上运行.因此,您正在阻塞 UI,等待一个在等待 UI 空闲时被阻塞的任务,这是一个典型的死锁.
Your problem is because you are blocking the UI thread when you call .Result
and you told the continuation after Task.Delay
to run on the UI thread. So you are blocking the UI waiting for a task that is blocked on waiting for the UI to become free, a classic deadlock.
两种解决方案.首先也让按钮点击异步.
Two solutions. First make the button click async too.
private async void buttonOk_Click(object sender, System.EventArgs e)
{
var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
if (await asyncResolvedIssue) {} // <== no deadlock!
}
事件处理程序是唯一允许您执行async void
的地方.
Event handlers are the only place you are allowed to do async void
.
另一个选项是通过设置 ConfigureAwait(bool)
为假.
The other option is tell Task.Delay
it does not need to have the rest of its function run on the UI thread by setting ConfigureAwait(bool)
to false.
public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
await Task.Delay(1000).ConfigureAwait(false);
return true;
}
现在 Task.Delay
之后的代码行将在线程池线程而不是 UI 线程上运行,并且不会因为 UI 线程当前被阻塞而被阻塞.
Now the line of code after the Task.Delay
will run on a threadpool thread instead of the UI thread and will not be blocked by the fact that the UI thread is currently blocked.
这篇关于Winforms 调用异步方法挂断程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!