En attente de plusieurs Tâches avec des résultats différents
J'ai 3 tâches:
private async Task<Cat> FeedCat() {}
private async Task<House> SellHouse() {}
private async Task<Tesla> BuyCar() {}
Ils ont tous besoin de courir avant de mon code peut continuer et j'ai besoin des résultats de chacun ainsi. Aucun de ces résultats n'ont rien en commun les uns avec les autres
Comment dois-je appeler et d'attendre pour les 3 tâches à accomplir et ensuite obtenir les résultats?
- Avez-vous de la commande exigence? C'est, ne vous en veux pas vendre la maison jusqu'à ce que après le chat est-il nourri?
Vous devez vous connecter pour publier un commentaire.
Après l'utilisation de
WhenAll
, vous pouvez tirer sur les résultats individuellement avecawait
:Vous pouvez également utiliser
Task.Result
(puisque vous savez en ce moment, ils ont tous terminé avec succès). Cependant, je vous recommande d'utiliserawait
parce que c'est clairement juste, tout enResult
peut causer des problèmes dans d'autres scénarios.WhenAll
à partir de ce entièrement; l'attend va prendre soin de vous assurant de ne pas dépasser les 3 plus tard affectations jusqu'à ce que les tâches sont toutes terminées.WhenAll
a une intention explicite.Result
clôturera l'exception d'origine à l'intérieur d'unAggregateException
, ce qui complique la gestion des erreurs.Task.WhenAll()
permet d'exécuter la tâche dans parallèle mode. Je ne comprends pas pourquoi @Servy a suggéré de le supprimer. Sans leWhenAll
ils seront exécutés un par uncatTask
est déjà en cours d'exécution par le temps, il est retourné à partir deFeedCat
. Donc, soit l'approche de travail - la seule question est de savoir si vous voulezawait
un par un ou tous ensemble. L'erreur de manipulation est légèrement différente - si vous utilisezTask.WhenAll
, alors il seraawait
tous, même si l'un d'eux ne parvient pas au début.WhenAll
n'a pas d'incidence sur le moment où les activités d'exécuter ou de la façon dont ils exécutent. que a toute permet s'effectuer de la façon dont les résultats sont observés. Dans ce cas particulier, la seule différence est qu'une erreur dans l'une des deux premières méthodes entraînerait l'exception levée dans cette pile d'appel plus tôt dans ma méthode de Stephen (bien que la même erreur serait de toujours être levée, si il y en a).WhenAll
travaillera en 5 secondes. D'autre part les exécuter EN CHAÎNE pour l'3+4+5=12 secondes. Comme quelqu'un a dit: les tâches ne doivent pas être liés les uns aux autres pour laWhenAll
. C'est pourquoi je penseWhenAll
a une influence majeure de la performance par exemple.WhenAll
elles sont exécutées IMMÉDIATEMENT. Sans elle, ils sont exécutés une fois que le précédent.await; + await; == await.ContinueWith(await;);
.ConfigureAwait(false)
à laWhenAll(tasks)
appel à garder à partir de blocage du thread. Ce serait la manière la plus optimale pour exécuter cette parallélisation.Task.WhenAny
.Task.WhenAll
n'est pas nécessaire. gist.github.com/aherrick/239647bf24bd6483924ce6a22f5c8edfWhenAll
. Mais la raison principale que j'en général, il est de sorte que le code est claire. Il est plus facile pour quelqu'un de familier avec elle pour comprendre l'intention et de la sémantique de la méthode, et donc le code est plus facile à maintenir.await Task.WhenAll(catTask, houseTask, carTask);
, ne serait-il pas un meilleur choix que d'appelercatTask.Result
plutôt qu'une trompeuseawait
? @StephenCleary, ne serait pas une exception, comme vous le mentionnez, être levée lorsque la.WhenAll
complète et donc il n'y a pas de mal à l'aide de.Wait
aprèsawait Task.WhenAll
?await
est plus résistant à la refactorisation, car il ne peut pas accidentellement devenu le blocage de code. Permettez-moi de renverser votre question. Étant donné queawait
est le naturel moyen de la consommation d'une tâche, quels sont les avantages d'Result
/Wait
ont le plus naturelawait
?await
, je suppose qu'il est nécessaire d'attendre un peut-être-tâche inachevée de retour. Dans notre cas, l'attente est déjà passé sur le.WhenAll
ligne, de sorte que le résultat est garanti d'être là, de façon synchrone. (Vous ne pouvez pas appeler.Wait
, seulement.Result
; veuillez pardonner ma faute de frappe.)Juste
await
les trois tâches séparément, après le démarrage de tous.Task.WhenAll
changements littéralement rien sur le comportement du programme, dans toutes les observables façon. C'est un purement redondant appel de méthode. Vous êtes invités à ajouter, si vous le souhaitez, comme un choix esthétique, mais il ne change pas ce que fait le code. Le temps d'exécution du code est identique avec ou sans cet appel de méthode (enfin, techniquement, il y a un vraiment petit de frais généraux pour les appelantWhenAll
, mais cela devrait être négligeable), ne faisant que de la version un peu plus de temps que cette version.WhenAll
en elle. Qui vous permettra de voir par vous-même ce qui l'exécute et quand.FeedCat
,SellHouse
etBuyCar
être fourni pour tester le code, vous pouvez l'utiliser pour chacun d'eux :Task FeedCat(){return Task.Delay(1000);}Task SellHouse(){return Task.Delay(1000);}Task BuyCar(){return Task.Delay(1000);}
. Vous pouvez utiliser ces méthodes pour les tests, et ensuite il suffit de copier-coller le code de la réponse dans une méthode et l'exécuter pour voir courir en 1 seconde, au lieu de 3.return Task.Delay...
qui je pensais était faux, parce que je ne pourrait le faire avecawait Task.Delay...
. Maintenant, je sais que vous voulait PAS la marque de la méthode asynchrone. De toute façon, cette discussion, c'est un gaspillage d'énergie à partir de ce point. Merci pour votre tempsWhenAll
est tout simplement inutile pour résoudre ce problème à l'aide de votre travail maintenant des méthodes asynchrones.await
travaille dans un fichier exécutable vs une application IIS. Quelle différence faites-vous pensez qu'il est pertinent? Bien sûr, cette réponse n'est pas conçu pour être un tutoriel qui couvre tous les éléments de base de la TPL. Ce n'est pas seulement au-delà de la portée de cette question spécifique, il est aussi au-delà de la portée de ce qui peut vraiment être fournies dans aucun donné de réponse. Cela dit, si vous avez une question plus spécifique quant à pourquoi, ni comment ce code fonctionne, puis par tous les moyens, posez-vous la question, et je vais faire de mon mieux pour répondre à votre question.WhenAll
est purement un changement esthétique. La seule différence observable dans le comportement est de savoir si vous attendre pour plus tard les tâches de finition, si une tâche antérieure défauts, ce qui n'est généralement pas nécessaire de le faire. Si vous ne croyez pas les nombreuses explications pourquoi votre déclaration n'est pas vrai, vous pouvez simplement exécuter le code, vous verrez que ce n'est pas vrai.Si vous êtes à l'aide de C# 7, vous pouvez utiliser une pratique wrapper méthode comme ceci...
...pour permettre la pratique de la syntaxe comme ça quand vous voulez attendre sur des tâches multiples avec différents types de retour. Vous auriez à faire plusieurs surcharges pour différents nombres de tâches d'attendre, bien sûr.
Result
propriétés de la condition des tâches après la tâche retourné parTask.WhenAll()
complète..Result
appels par Stephen raisonnement d'éviter d'autres personnes de perpétuer la mauvaise pratique par la copie de votre exemple.Vous pouvez les stocker dans des tâches, puis attendent tous:
var catTask = FeedCat()
exécuter la fonctionFeedCat()
et stocker le résultat danscatTask
faire laawait Task.WhenAll()
partie plutôt inutile, puisque la méthode a déjà été exécutée ??await Task.WhenAll
n'est pas nécessaire ici, les trois tâches commencé et après qui l'attendait, ensemble, vont se dérouler en parallèle.Donné trois tâches -
FeedCat()
,SellHouse()
etBuyCar()
, il y a deux cas: soit qu'elles aient terminé de façon synchrone (pour une raison quelconque, peut-être la mise en cache ou d'une erreur), soit ils ne le sont pas.Disons que nous avons, à partir de la question:
Maintenant, une approche simple serait:
mais ... qui n'est pas commode pour le traitement des résultats, nous serions souhaitez généralement
await
que:mais cela fait beaucoup de frais généraux et alloue les différents tableaux (y compris les
params Task[]
tableau) et les listes (en interne). Cela fonctionne, mais il n'est pas exceptionnel de l'OMI. Dans beaucoup de façons, il est plus simple d'utiliser unasync
fonctionnement et justeawait
chacun à leur tour:Contrairement à certains commentaires ci-dessus, à l'aide de
await
au lieu deTask.WhenAll
fait pas de différence à la façon dont les tâches s'exécutent (simultanément, de manière séquentielle, etc). Au plus haut niveau,Task.WhenAll
est antérieure à bonne prise en charge du compilateur pourasync
/await
, et a été utile lorsque ces choses n'existent pas. Il est également utile lorsque vous avez l'arbitraire d'un tableau de tâches, plutôt que 3 discrètes tâches.Mais: nous avons toujours le problème que
async
/await
génère beaucoup de compilateur de bruit pour la suite. Si il est probable que les tâches pourrait de terminer de façon synchrone, alors nous pouvons optimiser ce, en s'appuyant dans un synchrones chemin d'accès avec un asynchrone de secours:Cette "synchronisation de chemin avec async de secours" de l'approche est de plus en plus fréquent en particulier dans la haute performance code où synchrone les achèvements sont relativement fréquentes. Remarque il ne va pas aider du tout si la fin est toujours véritablement asynchrone.
Choses supplémentaires qui s'appliquent ici:
avec les récentes C#, un modèle commun est la
async
de secours méthode est couramment mis en œuvre comme une fonction locale:préfèrent
ValueTask<T>
àTask<T>
si il y a une bonne chance de choses jamais complètement de façon synchrone avec différentes valeurs de retour:si possible, préférez
IsCompletedSuccessfully
àStatus == TaskStatus.RanToCompletion
; ce qui existe maintenant dans .NET de Base pourTask
, et partout pourValueTask<T>
Task
quand ils sont tous fait sans l'aide des résultats.await
pour obtenir le "mieux" exception de la sémantique, sur l'hypothèse que les exceptions sont rares mais significatifsResult
laisser lever des exceptions comme il n'est pas souhaitable de la sémantique. Vous auriez besoin d'utiliserTask.FromException
pour construire le bon résultat. Il serait plus de travail (bien que le travail que vous pourriez tout simplement écrire une fois et le bâton dans une méthode, donc pas de que beaucoup plus de travail), mais je suppose que quelqu'un qui ne peut pas se permettre de prendre le temps de créer un objet si les tâches sont tous terminés veut l'éviter, même lorsque certains sont défectueuses ou de l'annulation.Dans le cas où vous essayez de vous connecter à toutes les erreurs, assurez-vous garder Tâche.WhenAll ligne dans votre code, beaucoup de commentaires suggèrent que vous pouvez le retirer et d'attendre pour des tâches individuelles. De la tâche.WhenAll est vraiment important pour la gestion des erreurs. Sans cette ligne, vous laissant potentiellement votre code ouvert pour non des exceptions.
Imaginer FeedCat throws exception dans le code suivant:
Dans ce cas, vous ne serez jamais vous attendent sur houseTask ni carTask. Il y a 3 scénarios possibles ici:
SellHouse est déjà terminée lorsque FeedCat a échoué. Dans
ce cas, vous êtes beaux.
SellHouse n'est pas complète et ne parvient pas, sauf exception, à un certain point. Exception n'est pas observée et seront relancés sur finaliseur fil.
SellHouse n'est pas complète et contient attend à l'intérieur. En cas
votre code s'exécute dans ASP.NET SellHouse échouent dès que certains de la
attend sera complété à l'intérieur. Cela se produit parce que vous avez
fait feu & oubliez pas d'appeler et de contexte de synchronisation a été perdu dès que FeedCat échoué.
Voici l'erreur que vous obtenez pour le cas (3):
Pour le cas (2), vous obtiendrez une erreur semblable, mais d'origine avec trace de pile d'exception.
Pour .NET 4.0 et versions ultérieures, vous pouvez prendre inaperçu exceptions à l'aide de TaskScheduler.UnobservedTaskException. Pour .NET 4.5 et plus inaperçu exceptions sont avalés par défaut pour les .NET 4.0 inaperçu exception crash de votre processus.
Plus de détails ici: La tâche de la gestion des exceptions dans .NET 4.5
Vous pouvez utiliser
Task.WhenAll
comme mentionné, ouTask.WaitAll
, selon que vous souhaitez que le thread d'attendre. Jetez un oeil sur le lien pour une explication des deux.WaitAll vs WhenAll
Utilisation
Task.WhenAll
et puis attendre les résultats:si vous souhaitez accéder au Chat, pour ce faire:
C'est très simple à faire et très utile pour l'utilisation, il n'est pas nécessaire d'aller chercher une solution complexe.
Avant D'Avertissement
Juste un petit headsup à ceux qui visitent ce type de threads à la recherche d'un moyen de paralléliser EntityFramework à l'aide async+attendre+de tâches ensemble d'outils de: Le modèle présenté ici est le son, cependant, quand il s'agit de la spéciale de flocon de neige de EF vous ne serez pas obtenir l'exécution en parallèle, à moins que et jusqu'à ce que vous utilisez une autre (nouvelle) db-contexte-exemple à l'intérieur de chaque et chaque *Asynchrone() l'appel en cause.
Ce genre de chose est nécessaire en raison inhérente limites de la conception de l'ef-db-contextes qui interdisent l'exécution de plusieurs requêtes en parallèle dans le même ef-db-instance de contexte.
Capitalisant sur les réponses déjà données, c'est la façon de vous assurer que vous collectez toutes les valeurs, même dans le cas où un ou plusieurs des tâches entraîne une exception:
Une alternative de mise en œuvre est plus ou moins les mêmes caractéristiques de performance pourrait être: