387 Stimmen

In Erwartung mehrerer Aufgaben mit unterschiedlichen Ergebnissen

Ich habe 3 Aufgaben:

private async Task FeedCat() {}
private async Task SellHouse() {}
private async Task BuyCar() {}

Sie müssen alle ausgeführt werden, bevor mein Code fortgesetzt werden kann, und ich benötige auch die Ergebnisse von jedem. Keine der Ergebnisse hat etwas gemeinsam miteinander

Wie rufe und warte ich auf die Beendigung der 3 Aufgaben und erhalte dann die Ergebnisse?

13voto

Reed Copsey Punkte 536986

Sie können sie in Aufgaben speichern und dann alle von ihnen abwarten:

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;

4voto

Christian Phillips Punkte 17356

Sie können Task.WhenAll verwenden, wie erwähnt, oder Task.WaitAll, je nachdem, ob Sie möchten, dass der Thread wartet. Schauen Sie sich den Link für eine Erklärung beider Möglichkeiten an.

WaitAll vs WhenAll

3voto

XDS Punkte 3083

Warnung vorwärts

Nur eine kurze Kopfnuss für diejenigen, die diese und andere ähnliche Threads besuchen und nach einem Weg suchen, um EntityFramework mithilfe des async+await+task-Toolsets zu parallelisieren: Das hier gezeigte Muster ist solide, jedoch werden Sie bei dem speziellen Schneeflocken-Problem von EF keine parallele Ausführung erreichen, es sei denn, Sie verwenden eine separate (neue) db-Kontext-Instanz in jedem einzelnen *Async() Aufruf.

Dies ist auf die inhärenten Designbeschränkungen von ef-db-Kontexten zurückzuführen, die das gleichzeitige Ausführen mehrerer Abfragen in derselben ef-db-Kontextinstanz verbieten.


Basiert auf den bereits gegebenen Antworten, so sammeln Sie alle Werte, selbst wenn einer oder mehrere der Tasks zu einer Ausnahme führen:

 public async Task Foobar() {
    async Task Erwartet(Task a, Task b, Task c) {
        return MachEtwas(await a, await b, await c);
    }

    using (var carTask = KaufAutoAsync())
    using (var catTask = FüttereKatzeAsync())
    using (var houseTask = HausVerkaufenAsync())
    {
        if (carTask.Status == TaskStatus.RanToCompletion //dreifach
            && catTask.Status == TaskStatus.RanToCompletion //cache
            && houseTask.Status == TaskStatus.RanToCompletion) { //treffer
            return Task.FromResult(MachEtwas(catTask.Result, carTask.Result, houseTask.Result)); //schnellzugriff
        }

        cat = await catTask;
        car = await carTask;
        house = await houseTask;
        //oder Task.AwaitAll(carTask, catTask, houseTask);
        //oder await Task.WhenAll(carTask, catTask, houseTask);
        //es hängt davon ab, wie Sie die Fehlerbehandlung lieber mögen

        return Erwartet(catTask, carTask, houseTask);
   }
 }

Ein alternativer Ansatz, der mehr oder weniger die gleiche Leistungsfähigkeit hat, könnte sein:

 public async Task Foobar() {
    using (var carTask = KaufAutoAsync())
    using (var catTask = FüttereKatzeAsync())
    using (var houseTask = HausVerkaufenAsync())
    {
        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 MachEtwas(cat, car, house);
     }
 }

0voto

It'sNotALie. Punkte 21449

Verwenden Sie Task.WhenAll und warten Sie dann auf die Ergebnisse:

var tCat = FeedCat();
var tHouse = SellHouse();
var tCar = BuyCar();
await Task.WhenAll(tCat, tHouse, tCar);
Cat cat = await tCat;
House haus = await tHouse;
Tesla auto = await tCar; 
//da sie alle definitiv fertig sind, könnten Sie auch Task.Value verwenden.

0voto

Theodor Zoulias Punkte 22972

Die drei Aufgaben in Ihrem Beispiel unterscheiden sich erheblich in ihrer Bedeutung. Falls eine davon fehlschlägt, möchten Sie wahrscheinlich wissen, was mit den anderen passiert ist. Zum Beispiel, wenn die Kommunikation mit dem automatischen Katzenfutterautomaten fehlschlägt, möchten Sie nicht verpassen, ob der Verkauf Ihres Hauses erfolgreich oder erfolglos war. Daher macht es Sinn, nicht nur eine Katze, ein Haus und ein Tesla zurückzugeben, sondern die Aufgaben selbst. Der Aufrufercode kann dann jede der drei Aufgaben separat abfragen und angemessen auf ihre erfolgreiche oder fehlgeschlagene Fertigstellung reagieren:

public async Task<(Task, Task, Task)> FeedCatSellHouseBuyCar()
{
    Task aufgabe1 = KatzeFüttern();
    Task aufgabe2 = HausVerkaufen();
    Task aufgabe3 = AutoKaufen();

    // Alle drei Aufgaben werden an dieser Stelle gestartet.

    try { await Task.WhenAll(aufgabe1, aufgabe2, aufgabe3).ConfigureAwait(false); } catch { }

    // Alle drei Aufgaben sind zu diesem Zeitpunkt abgeschlossen.

    return (aufgabe1, aufgabe2, aufgabe3);
}

Verwendungsbeispiel:

var (katzenAufgabe, hausAufgabe, teslaAufgabe) = await FeedCatSellHouseBuyCar();

// Alle drei Aufgaben sind zu diesem Zeitpunkt abgeschlossen.

if (katzenAufgabe.IsCompletedSuccessfully)
    Console.WriteLine($"{katzenAufgabe.Result.Name} frisst ihr gesundes Essen.");
else
    Console.WriteLine("Ihre Katze hungert!");

if (hausAufgabe.IsCompletedSuccessfully)
    Console.WriteLine($"Ihr Haus in {hausAufgabe.Result.Address} wurde verkauft. Sie sind jetzt reich und obdachlos!");
else
    Console.WriteLine("Sie sind immer noch der arme Besitzer Ihres Hauses.");

if (teslaAufgabe.IsCompletedSuccessfully)
    Console.WriteLine("Sie sind jetzt Besitzer eines batteriebetriebenen {teslaAufgabe.Result.Name}.");
else
    Console.WriteLine("Sie fahren immer noch einen Hyundai.");

Der try-Block mit dem leeren catch ist erforderlich, weil .NET 7 immer noch keine richtige Möglichkeit bietet, eine Aufgabe zu await ohne im Falle von Abbruch oder Fehlschlag eine Ausnahme auszulösen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X