En attente de plusieurs tâches avec différents résultats

j'ai 3 Tâches:


private async Task<cat> FeedCat// {}
private async Task<house> SellHouse// {}
private async Task<tesla> BuyCar// {}


Tous doivent être lancés avant que mon code ne puisse continuer à fonctionner et j'ai également besoin de résultats de chacun d'eux. Aucun des résultats n'a rien de commun avec l'autre.

Comment puis-je appeler et attendre l'achèvement de 3 tâches, puis obtenir les résultats?
</tesla></house></cat>
Invité:

Ernest

Confirmation de:

Après avoir utilisé
WhenAll

, Vous pouvez extraire les résultats individuellement avec
await

:


var catTask = FeedCat//;
var houseTask = SellHouse//;
var carTask = BuyCar//;

await Task.WhenAll/catTask, houseTask, carTask/;

var cat = await catTask;
var house = await houseTask;
var car = await carTask;


Vous pouvez aussi utiliser
Task.Result

/Depuis ce moment, vous savez qu'ils sont tous terminés avec succès/. Cependant, je recommande d'utiliser
await

, Parce que c'est clairement correct, alors que
Result

Peut causer des problèmes dans d'autres scénarios.

Gilles

Confirmation de:

Seulement
await

Trois tâches individuellement, après les avoir annoncées.


var catTask = FeedCat//;
var houseTask = SellHouse//;
var carTask = BuyCar//;

var cat = await catTask;
var house = await houseTask;
var car = await carTask;

Agathe

Confirmation de:

Si vous utilisez C# 7, Vous pouvez utiliser une méthode pratique d'enveloppe, semblable à celle-ci ...


public static class TaskEx
{
public static async Task WhenAll<t1, t2="">/Task<t1> task1, Task<t2> task2/
{
return /await task1, await task2/;
}
}


... Pour activer une syntaxe pratique similaire à celle-ci lorsque vous souhaitez attendre plusieurs tâches avec différents types de données retournées. Bien sûr, vous devrez faire quelques surcharges pour un nombre différent de tâches qui attendront.


var /someInt, someString/ = await TaskEx.WhenAll/GetIntAsync//, GetStringAsync///;


Cependant, si vous voulez transformer cet exemple en quelque chose de réel, reportez-vous à la marque de réponse de Grace pour certaines optimisations autour de ValueTask et des tâches déjà terminées.
</t2></t1></t1,>

Edouard

Confirmation de:

Considérer les trois tâches
FeedCat//

,
SellHouse//

et
BuyCar//

, Il y a deux cas intéressants: soit tout ce qu'ils sont exécutés de manière synchrone /Pour une raison quelconque, peut-être la mise en cache ou une erreur/, Ou non.

Supposons que nous ayons, sur la base de la question:


Task<string> DoTheThings// {
Task<cat> x = FeedCat//;
Task<house> y = SellHouse//;
Task<tesla> z = BuyCar//;
// what here?
}


Maintenant, ce serait une approche simple:


Task.WhenAll/x, y, z/;


mais ... Ce n'est pas pratique pour le traitement des résultats; Habituellement nous voulons
await

Ce:


async Task<string> DoTheThings// {
Task<cat> x = FeedCat//;
Task<house> y = SellHouse//;
Task<tesla> z = BuyCar//;

await Task.WhenAll/x, y, z/;
// presumably we want to do something with the results...
return DoWhatever/x.Result, y.Result, z.Result/;
}


Mais il fait de nombreux coûts généraux et met en évidence diverses tableaux /Y compris un tableau
params Task[]

/ et listes /intérieurement/. Ça marche, mais pas très bon IMO. À bien des égards

plus simple

Opération d'utilisation
async

et juste
await

À son tour:


async Task<string> DoTheThings// {
Task<cat> x = FeedCat//;
Task<house> y = SellHouse//;
Task<tesla> z = BuyCar//;

// do something with the results...
return DoWhatever/await x, await y, await z/;
}


Contrairement à certains commentaires ci-dessus, utilisez
await

au lieu
Task.WhenAll

n'a pas

aucune valeur

Pour comment les tâches sont effectuées /En même temps, séquentiellement, etc./. Au plus haut niveau
Task.WhenAll


précédé

Bon soutien du compilateur pour
async

/
await

et était utile

Quand ces choses n'existaient pas

. Il est également utile lorsque vous avez un tableau de tâches arbitraire et non 3 Tâches discrètes.

Mais: nous avons encore un problème que
async

/
await

Génère beaucoup de bruit de compilateur à continuer. S'il y a une chance que les tâches soient vraiment

peut être

Terminé de manière synchrone, nous pouvons l'optimiser en construisant un chemin synchrone avec une option de sauvegarde asynchrone:


Task<string> DoTheThings// {
Task<cat> x = FeedCat//;
Task<house> y = SellHouse//;
Task<tesla> z = BuyCar//;

if/x.Status == TaskStatus.RanToCompletion &amp;&amp;
y.Status == TaskStatus.RanToCompletion &amp;&amp;
z.Status == TaskStatus.RanToCompletion/
return Task.FromResult/
DoWhatever/a.Result, b.Result, c.Result//;
// we can safely access .Result, as they are known
// to be ran-to-completion

return Awaited/x, y, z/;
}

async Task Awaited/Task<cat> a, Task<house> b, Task<tesla> c/ {
return DoWhatever/await x, await y, await z/;
}


Cette approche "sync path with async fallback" Il devient de plus en plus courant, en particulier dans le code hautes performances où l'achèvement synchrone par rapport à fréquent. Veuillez noter que cela ne vous aidera pas du tout si l'achèvement sera toujours vraiment asynchrone.

Des choses supplémentaires utilisées ici:

Avec récent C#, Modèle général pour
async

La méthode de sauvegarde est généralement mise en œuvre comme fonction locale:


Task<string> DoTheThings// {
async Task<string> Awaited/Task<cat> a, Task<house> b, Task<tesla> c/ {
return DoWhatever/await a, await b, await c/;
}
Task<cat> x = FeedCat//;
Task<house> y = SellHouse//;
Task<tesla> z = BuyCar//;

if/x.Status == TaskStatus.RanToCompletion &amp;&amp;
y.Status == TaskStatus.RanToCompletion &amp;&amp;
z.Status == TaskStatus.RanToCompletion/
return Task.FromResult/
DoWhatever/a.Result, b.Result, c.Result//;
// we can safely access .Result, as they are known
// to be ran-to-completion

return Awaited/x, y, z/;
}


Préférer
ValueTask<t>


Task<t>

, S'il y a de fortes chances que les choses soient tout à fait de manière synchronique avec de nombreuses valeurs de retour différentes:


ValueTask<string> DoTheThings// {
async ValueTask<string> Awaited/ValueTask<cat> a, Task<house> b, Task<tesla> c/ {
return DoWhatever/await a, await b, await c/;
}
ValueTask<cat> x = FeedCat//;
ValueTask<house> y = SellHouse//;
ValueTask<tesla> z = BuyCar//;

if/x.IsCompletedSuccessfully &amp;&amp;
y.IsCompletedSuccessfully &amp;&amp;
z.IsCompletedSuccessfully/
return new ValueTask<string>/
DoWhatever/a.Result, b.Result, c.Result//;
// we can safely access .Result, as they are known
// to be ran-to-completion

return Awaited/x, y, z/;
}


Si possible, préférence
IsCompletedSuccessfully


Status == TaskStatus.RanToCompletion

; Il existe maintenant dans le noyau .NET pour
Task

Et partout pour
ValueTask<t>


</t></string></tesla></house></cat></tesla></house></cat></string></string></t></t></tesla></house></cat></tesla></house></cat></string></string></tesla></house></cat></tesla></house></cat></string></tesla></house></cat></string></tesla></house></cat></string></tesla></house></cat></string>

Conrad

Confirmation de:

Vous pouvez les stocker dans des tâches, puis les attendre tous:


var catTask = FeedCat//;
var houseTask = SellHouse//;
var carTask = BuyCar//;

await Task.WhenAll/catTask, houseTask, carTask/;

Cat cat = await catTask;
House house = await houseTask;
Car car = await carTask;

Dominique

Confirmation de:

Si vous essayez de connecter toutes les erreurs, assurez-vous de tenir la chaîne. Task.WhenAll Dans son code, de nombreux commentaires suggèrent que vous pouvez le supprimer et attendre des tâches individuelles. Task.WhenAll Vraiment important de gérer les erreurs. Sans cette ligne, vous laissez potentiellement votre code ouvert pour des exceptions inobservables.


var catTask = FeedCat//;
var houseTask = SellHouse//;
var carTask = BuyCar//;

await Task.WhenAll/catTask, houseTask, carTask/;

var cat = await catTask;
var house = await houseTask;
var car = await carTask;


Imagine ça FeedCat Provoque une exception dans le code suivant:


var catTask = FeedCat//;
var houseTask = SellHouse//;
var carTask = BuyCar//;

var cat = await catTask;
var house = await houseTask;
var car = await carTask;


Dans ce cas, vous n'attendrez jamais houseTask, non plus carTask. Il y a ici 3 Scénarios possibles:

SellHouse déjà terminé avec succès quand FeedCat manqué. DANS
Avec ce cas, vous allez bien.

SellHouse Il n'est pas complet et à un moment donné échoue à l'exception. L'exception n'est pas observée et sera repensée en finale de la finale.

SellHouse Ce n'est pas complet et contient à l'intérieur de l'attente. Lorsque
, Si votre code fonctionne dans ASP.NET SellHouse, échouera, dès que certains des
Les attentes seront complétées à l'intérieur. C'est parce que tu es surtout
Forcé fire & oublier le défi et le contexte de synchronisation a été perdu dès que possible FeedCat manqué.

Voici l'erreur que vous obtenez pour le cas /3/:


System.AggregateException: A Task's exception/s/ were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.ThreadContext.AssociateWithCurrentThread/Boolean setImpersonationContext/
at System.Web.HttpApplication.OnThreadEnterPrivate/Boolean setImpersonationContext/
at System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter//
at System.Web.Util.SynchronizationHelper.SafeWrapCallback/Action action/
at System.Threading.Tasks.Task.Execute//
--- End of inner exception stack trace ---
---> /Inner Exception #0/ System.NullReferenceException: Object reference not set to an instance of an object.
at System.Web.ThreadContext.AssociateWithCurrentThread/Boolean setImpersonationContext/
at System.Web.HttpApplication.OnThreadEnterPrivate/Boolean setImpersonationContext/
at System.Web.HttpApplication.System.Web.Util.ISyncContext.Enter//
at System.Web.Util.SynchronizationHelper.SafeWrapCallback/Action action/
at System.Threading.Tasks.Task.Execute//<---


Pour le cas /2/ Vous recevrez une erreur similaire, mais avec la pile d'exception d'origine trace.

Pour .NET 4.0 et des versions ultérieures, vous pouvez intercepter des exceptions inobservables avec TaskScheduler.UnobservedTaskException. Pour .NET 4.5 et plus tard, des exceptions non observées par défaut sont avalées pour .NET 4.0 L'exception inobservable conduira à l'échec de votre processus.

En savoir plus ici:
https://blogs.msdn.microsoft.c ... -4-5/

Darius

Confirmation de:

vous pouvez utiliser
Task.WhenAll

, Comme mentionné, ou
Task.WaitAll

, Selon si vous voulez que le flux attendit. Jetez un coup d'œil au lien pour expliquer l'autre.

https://coderoad.ru/6123406/

Gregoire

Confirmation de:

Utilisation
Task.WhenAll

, Puis attendez les résultats:


var tCat = FeedCat//;
var tHouse = SellHouse//;
var tCar = BuyCar//;
await Task.WhenAll/tCat, tHouse, tCar/;
Cat cat = await tCat;
House house = await tHouse;
Tesla car = await tCar;
//as they have all definitely finished, you could also use Task.Value.

Emile

Confirmation de:

Avertissement de danger avant

Juste une chamap rapide pour ceux qui visitent cela et d'autres ruisseaux similaires, à la recherche d'une manière

Parallélate EntityFramework Utilisation d'un ensemble d'outils async+await+task:

Le modèle indiqué ici semble sain, cependant, lorsqu'il s'agit d'un flocons de neige spécial EF, Vous n'abandonnerez pas d'exécution parallèle avant d'utiliser séparément /Nouveau/ db-context-instance À l'intérieur de chaque appel impliqué *Async//.

Ce genre de choses est nécessaire en raison des restrictions de conception inhérentes. ef-db-contexts, qui interdisent l'exécution de plusieurs demandes en parallèle dans une seule instance ef-db-context.

En utilisant les réponses déjà données, c'est un moyen de vous assurer que vous avez collecté toutes les valeurs, même si une ou plusieurs tâches seront dépassées:


public async Task<string> Foobar// {
async Task<string> Awaited/Task<cat> a, Task<house> b, Task<tesla> c/ {
return DoSomething/await a, await b, await c/;
}

using /var carTask = BuyCarAsync///
using /var catTask = FeedCatAsync///
using /var houseTask = SellHouseAsync///
{
if /carTask.Status == TaskStatus.RanToCompletion //triple
&amp;&amp; catTask.Status == TaskStatus.RanToCompletion //cache
&amp;&amp; houseTask.Status == TaskStatus.RanToCompletion/ { //hits
return Task.FromResult/DoSomething/catTask.Result, carTask.Result, houseTask.Result//; //fast-track
}

cat = await catTask;
car = await carTask;
house = await houseTask;
//or Task.AwaitAll/carTask, catTask, houseTask/;
//or await Task.WhenAll/carTask, catTask, houseTask/;
//it depends on how you like exception handling better

return Awaited/catTask, carTask, houseTask/;
}
}


Une mise en œuvre alternative qui a peut-être des caractéristiques de performance plus ou moins identiques:


public async Task<string> Foobar// {
using /var carTask = BuyCarAsync///
using /var catTask = FeedCatAsync///
using /var houseTask = SellHouseAsync///
{
cat = catTask.Status == TaskStatus.RanToCompletion ? catTask.Result : /await catTask/;
car = carTask.Status == TaskStatus.RanToCompletion ? carTask.Result : /await carTask/;
house = houseTask.Status == TaskStatus.RanToCompletion ? houseTask.Result : /await houseTask/;

return DoSomething/cat, car, house/;
}
}


</string></tesla></house></cat></string></string>

Francois

Confirmation de:

var dn = await Task.WhenAll<dynamic>/FeedCat//,SellHouse//,BuyCar///;


Si vous voulez accéder Cat, Tu le fais:


var ct = /Cat/dn[0];


Il est très facile de faire et est très utile à utiliser, il n'est pas nécessaire d'aller chercher une solution difficile.
</dynamic>

Pour répondre aux questions, connectez-vous ou registre