Winforms调用异步方法挂起程序

bas*_*bas 31 c# deadlock async-await

我一直在解决这个问题,但现在我真的想了解出了什么问题.我有一个相当简单的应用程序(它是youtrack的turtoise SVN插件,但我可以用一个简单的winforms应用程序重现这个问题).

我有一个异步方法 ResolveIssue

public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
    await Task.Delay(1000);

    return true;
}
Run Code Online (Sandbox Code Playgroud)

创建死锁所需要做的就是在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!
}
Run Code Online (Sandbox Code Playgroud)

现在我明白拥有异步方法并主动等待它是相当奇怪的,但为什么会产生死锁?!

Sco*_*ain 44

您的问题是因为您在调用时阻止了UI线程,.Result并且您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!
}
Run Code Online (Sandbox Code Playgroud)

事件处理程序是您唯一可以执行的操作async void.

另一个选项是告诉Task.Delay它不需要通过设置ConfigureAwait(bool)为false 在UI线程上运行其余的功能.

public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
    await Task.Delay(1000).ConfigureAwait(false);

    return true;
}
Run Code Online (Sandbox Code Playgroud)

现在,Task.Delay将在线程池线程而不是UI线程上运行代码行,并且不会被UI线程当前被阻止的事实阻止.

  • 对我来说很遗憾,因为我无法使我的点击事件处理程序异步. (7认同)