web-dev-qa-db-fra.com

Que se passe-t-il en attendant le résultat d'une tâche?

J'utilise HttpClient pour publier des données sur un service distant dans un projet .NET 4.0. Je ne suis pas concerné par le blocage de cette opération, j'ai donc pensé que je pouvais ignorer ContinueWith ou async/wait et utiliser Result.

Lors du débogage, j'ai rencontré un problème où le serveur distant n'était pas réactif. Alors que je parcourais le code, il semblait que mon code venait de s'arrêter sur la troisième ligne ... la ligne actuelle du pointeur de pile a cessé d'être surlignée en jaune et n'a pas avancé à la ligne suivante. Il a juste disparu. Il m'a fallu un certain temps pour réaliser que je devais attendre que la demande expire.

var client = new HttpClient();
var task = client.PostAsync("http://someservice/", someContent);
var response = task.Result;

Ma compréhension était que l'appel de Result sur la tâche faisait que le code s'exécutait de manière synchrone, se comportait plus comme ceci (je sais qu'il n'y a pas de méthode Post dans HttpClient):

var client = new HttpClient();
var response = client.Post("http://someservice/", someContent);

Je ne suis pas sûr que ce soit une mauvaise chose, j'essaie juste de comprendre. Est-il vraiment vrai qu'en raison du fait que le HttpClient renvoie directement des tâches au lieu des résultats, mon application profite automatiquement de l'asynchronie même lorsque je pense que je l'évite?

36
scottt732

Sous Windows, toutes les E/S sont asynchrones. Les API synchrones ne sont qu'une abstraction pratique.

Ainsi, lorsque vous utilisez HttpWebRequest.GetResponse, Ce qui se passe réellement, c'est que les E/S sont démarrées (de manière asynchrone) et le thread appelant (de manière synchrone) se bloque, en attendant qu'il se termine.

De même, lorsque vous utilisez HttpClient.PostAsync(..).Result, les E/S sont démarrées (de manière asynchrone) et le thread appelant (de manière synchrone) se bloque, en attendant qu'il se termine.

Je recommande généralement aux gens d'utiliser await plutôt que Task.Result Ou Task.Wait Pour les raisons suivantes:

  1. Si vous bloquez sur un Task qui est le résultat d'une méthode async, vous pouvez entrer facilement dans une situation de blocage .
  2. Task.Result Et Task.Wait Encapsulent toutes les exceptions dans un AggregateException (car ces API sont des restes du TPL). La gestion des erreurs est donc plus complexe.

Cependant, si vous êtes conscient de ces limitations, il existe certaines situations où le blocage sur un Task peut être utile (par exemple, dans le Main d'une application console).

43
Stephen Cleary

La capture du résultat d'une tâche bloque le thread actuel. Il est inutile d'utiliser une version asynchrone d'une méthode dans ce cas. Post() et PostAsync().Result bloqueront tous les deux.

Si vous souhaitez utiliser la simultanéité, vous devez l'écrire comme telle:

async Task PostContent()
{
  var client = new HttpClient();
  Task t = await client.PostAsync("http://someservice/", someContent);
  //code after this line will execute when the PostAsync completes.
  return t;
}

Puisque PostContent() lui-même renvoie une tâche, la méthode qui l'appelle doit également attendre.

async void ProcessResult()
{
   var result = await PostContent(); 
   //Do work with the result when the result is ready 
}

Par exemple, si vous appelez ProcessResult() dans un gestionnaire de clic de bouton, vous voyez que l'interface utilisateur est toujours sensible, d'autres contrôles fonctionnent toujours.

2
AtillaBaspinar