web-dev-qa-db-fra.com

L'appel de Winforms à la méthode async raccroche le programme

Je travaille autour de ce problème depuis un certain temps, mais maintenant j'aimerais vraiment comprendre ce qui ne va pas. J'ai une application assez simple (c'est un plugin SVN turtoise pour youtrack, mais je peux reproduire le problème avec une application winforms triviale).

J'ai une méthode asynchrone ResolveIssue

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

    return true;
}

Tout ce que je dois faire pour créer un blocage est d'appeler cette méthode asynchrone dans un gestionnaire d'événements Button et d'appeler Task.Wait ou Task.Result, comme ça

private void buttonOk_Click(object sender, System.EventArgs e)
{
    var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
    if (asyncResolvedIssue.Result) {} // <== deadlock!
}

Maintenant, je comprends que c'est plutôt bizarre d'avoir une méthode asynchrone et de l'attendre activement, mais pourquoi cela générerait-il un blocage?!

31
bas

Votre problème est dû au fait que vous bloquez le thread d'interface utilisateur lorsque vous appelez .Result Et que vous avez indiqué la poursuite après Task.Delay De l'exécution sur le thread d'interface utilisateur. Vous bloquez donc l'interface utilisateur en attente d'une tâche qui est bloquée en attendant que l'interface utilisateur devienne gratuite, un blocage classique.

Deux solutions. Faites d'abord cliquer le bouton sur async aussi.

private async void buttonOk_Click(object sender, System.EventArgs e)
{
    var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
    if (await asyncResolvedIssue) {} // <== no deadlock!
}

Les gestionnaires d'événements sont le seul endroit où vous êtes autorisé à faire async void.

L'autre option est de dire à Task.Delay Qu'il n'a pas besoin d'exécuter le reste de sa fonction sur le thread d'interface utilisateur en définissant ConfigureAwait(bool) sur false.

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

    return true;
}

Maintenant, la ligne de code après le Task.Delay S'exécutera sur un thread threadpool au lieu du thread d'interface utilisateur et ne sera pas bloquée par le fait que le thread d'interface utilisateur est actuellement bloqué.

43
Scott Chamberlain