web-dev-qa-db-fra.com

Quelle est la différence entre un retour nul et un retour d'une tâche?

En regardant divers exemples CTP Async CTP, je vois certaines fonctions asynchrones qui renvoient void, et d'autres qui renvoient le Task non générique. Je peux voir pourquoi retourner un Task<MyType> est utile pour renvoyer des données à l'appelant lorsque l'opération asynchrone est terminée, mais les fonctions que j'ai vues qui ont un type de retour Task ne retournent jamais de données. Pourquoi ne pas retourner void?

120
James Cadd

Les réponses de SLaks et Killercam sont bonnes; Je pensais simplement ajouter un peu plus de contexte.

Votre première question concerne essentiellement les méthodes pouvant être marquées async.

Une méthode marquée comme async peut renvoyer void, Task ou Task<T>. Quelles sont les différences entre eux?

Une méthode asynchrone de retour Task<T> Peut être attendue, et lorsque la tâche sera terminée, elle offrira un T.

Une Task renvoyant une méthode asynchrone peut être attendue, et lorsque la tâche est terminée, la poursuite de la tâche est planifiée pour s'exécuter.

Une void retournant une méthode asynchrone ne peut pas être attendue; c'est une méthode "feu et oublie". Cela fonctionne de manière asynchrone et vous n'avez aucun moyen de savoir quand cela est fait. C'est plus qu'un peu bizarre; comme le dit SLaks, normalement vous ne feriez cela que lorsque vous créez un gestionnaire d'événements asynchrone. L'événement se déclenche, le gestionnaire s'exécute; personne n'attendra la tâche renvoyée par le gestionnaire d'événements car les gestionnaires d'événements ne renvoient pas de tâches, et même s'ils le faisaient, quel code utiliserait la tâche pour quelque chose? Ce n'est généralement pas le code utilisateur qui transfère le contrôle au gestionnaire en premier lieu.

Votre deuxième question, dans un commentaire, concerne essentiellement ce qui peut être awaited:

Quels types de méthodes peuvent être awaited? Une méthode de retour vide peut-elle être awaited?

Non, une méthode de retour vide ne peut pas être attendue. Le compilateur traduit await M() en un appel à M().GetAwaiter(), où GetAwaiter peut être une méthode d'instance ou une méthode d'extension. La valeur attendue doit être celle pour laquelle vous pouvez obtenir un serveur; il est clair qu'une méthode de retour de vide ne produit pas de valeur à partir de laquelle vous pouvez obtenir un serveur.

Task- les méthodes de retour peuvent produire des valeurs attendues. Nous prévoyons que des tiers voudront créer leurs propres implémentations d'objets de type Task qui peuvent être attendus, et vous pourrez les attendre. Cependant, vous ne serez pas autorisé à déclarer des méthodes async qui renvoient autre chose que void, Task ou Task<T>.

(MISE À JOUR: Ma dernière phrase pourrait être falsifiée par une future version de C #; il est proposé d'autoriser des types de retour autres que les types de tâche pour les méthodes asynchrones.)

(MISE À JOUR: La fonctionnalité mentionnée ci-dessus est arrivée en C # 7.)

201
Eric Lippert

Dans le cas où l'appelant souhaite attendre la tâche ou ajouter une continuation.

En fait, la seule raison de renvoyer void est si vous ne pouvez pas retourner Task parce que vous écrivez un gestionnaire d'événements.

22
SLaks

Les méthodes renvoyant Task et Task<T> Sont composables - ce qui signifie que vous pouvez await dans une méthode async.

Les méthodes async renvoyant void ne sont pas composables, mais elles ont deux autres propriétés importantes:

  1. Ils peuvent être utilisés comme gestionnaires d'événements.
  2. Ils représentent une opération asynchrone de "haut niveau".

Le deuxième point est important lorsque vous traitez avec un contexte qui maintient un nombre d'opérations asynchrones en suspens.

Le contexte ASP.NET est l'un de ces contextes; si vous utilisez des méthodes asynchrones Task sans les attendre d'une méthode asynchrone void, la demande ASP.NET sera terminée trop tôt.

Un autre contexte est le AsyncContext que j'ai écrit pour les tests unitaires (disponible ici ) - la méthode AsyncContext.Run Suit le nombre d'opérations en suspens et retourne quand il est zéro.

18
Stephen Cleary

Tapez Task<T> est le type cheval de bataille de la bibliothèque parallèle de tâches (TPL), il représente le concept de "certains travaux/travaux qui produiront un résultat de type T à l'avenir". Le concept de "travail qui s'achèvera à l'avenir mais ne renvoie aucun résultat" est représenté par le type de tâche non générique.

Précisément comment le résultat de type T va être produit et détail d'implémentation d'une tâche particulière; le travail peut être transféré vers un autre processus sur la machine locale, vers un autre thread, etc. Les tâches TPL sont généralement transférées vers les threads de travail d'un pool de threads dans le processus en cours, mais ce détail d'implémentation n'est pas fondamental pour le Task<T> type; plutôt un Task<T> peut représenter toute opération à latence élevée qui produit un T.

Sur la base de votre commentaire ci-dessus:

L'expression await signifie "évaluer cette expression pour obtenir un objet représentant un travail qui produira à l'avenir un résultat. Enregistrez le reste de la méthode actuelle comme rappel associé à la poursuite de cette tâche. Une fois cette tâche effectuée est produit et le rappel est signé, immédiatement rendre le contrôle à mon appelant ". Ceci est opposé/contrairement à un appel de méthode normal, ce qui signifie "rappelez-vous ce que vous faites, exécutez cette méthode jusqu'à ce qu'elle soit complètement terminée, puis reprenez là où vous vous étiez arrêté, connaissant maintenant le résultat de la méthode".


Edit: Je devrais citer l'article d'Eric Lippert en octobre 2011 MSDN Magazine car cela m'a beaucoup aidé à comprendre ce genre de choses en premier lieu.

Pour plus d'informations et de pages blanches, voir ici .

J'espère que cela vous sera utile.

12
MoonKnight