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
?
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 renvoyervoid
,Task
ouTask<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 await
ed:
Quels types de méthodes peuvent être
await
ed? Une méthode de retour vide peut-elle êtreawait
ed?
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.)
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.
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:
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.
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.