Winforms call to async method hangs up program

bas picture bas · Aug 23, 2014 · Viewed 13.2k times · Source

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).

I have an async method ResolveIssue

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

    return true;
}

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?!

Answer

Scott Chamberlain picture Scott Chamberlain · Aug 23, 2014

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!
}

Event handlers are the only place you are allowed to do async void.

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;
}

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.