387 Stimmen

In Erwartung mehrerer Aufgaben mit unterschiedlichen Ergebnissen

Ich habe 3 Aufgaben:

private async Task FüttereKatze() {}
private async Task VerkaufeHaus() {}
private async Task KaufeAuto() {}

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 irgendetwas gemeinsam miteinander

Wie rufe ich die 3 Aufgaben auf, warte auf deren Abschluss und erhalte dann die Ergebnisse?

13voto

Reed Copsey Punkte 536986

Sie können sie in Aufgaben speichern und dann alle darauf warten:

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 bereits erwähnt, oder Task.WaitAll, je nachdem, ob Sie möchten, dass der Thread wartet. Werfen Sie einen Blick auf den Link für eine Erklärung beider.

WaitAll vs WhenAll

3voto

XDS Punkte 3083

Forward Warning

Nur eine schnelle Warnung an diejenigen, die diesen und andere ähnliche Threads besuchen und nach einem Weg suchen, EntityFramework mithilfe des async+await+task-Toolsets zu parallelisieren: Das hier gezeigte Muster ist solide, jedoch werden Sie bei dem speziellen EF-Sonderfall keine parallele Ausführung erreichen, es sei denn, Sie verwenden eine separate (neue) db-Kontext-Instanz innerhalb jedes einzelnen *Async()-Aufrufs.

Dies ist auf die inhärenten Designeinschränkungen von ef-db-Kontexten zurückzuführen, die das Ausführen mehrerer Abfragen parallel in der gleichen ef-db-Kontext-Instanz verbieten.


Basierend auf den bereits gegebenen Antworten ist dies der Weg, um sicherzustellen, dass Sie alle Werte sammeln, auch wenn eine oder mehrere der Aufgaben zu einer Ausnahme führen:

  public async Task Foobar() {
    async Task Awaited(Task a, Task b, Task 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
            && catTask.Status == TaskStatus.RanToCompletion //cache
            && 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;
        //oder Task.AwaitAll(carTask, catTask, houseTask);
        //oder await Task.WhenAll(carTask, catTask, houseTask);
        //je nachdem, wie Sie die Fehlerbehandlung bevorzugen

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

Eine alternative Implementierung, die mehr oder weniger die gleichen Leistungsmerkmale aufweist, könnte sein:

 public async Task 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);
     }
 }

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 house = await tHouse;
Tesla car = 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 Katzenfuttergerät fehlschlägt, möchten Sie nicht verpassen, ob der Verkauf Ihres Hauses erfolgreich oder gescheitert ist. Es macht also Sinn, nicht nur eine Cat, ein House und ein Tesla zurückzugeben, sondern die Aufgaben selbst. Der aufrufende Code kann dann jede der drei Aufgaben separat abfragen und entsprechend auf ihren erfolgreichen oder fehlgeschlagenen Abschluss reagieren:

public async Task<(Task, Task, Task)> FeedCatSellHouseBuyCar()
{
    Task task1 = FeedCat();
    Task task2 = SellHouse();
    Task task3 = BuyCar();

    // Alle drei Aufgaben werden an diesem Punkt gestartet.

    try { await Task.WhenAll(task1, task2, task3).ConfigureAwait(false); } catch { }

    // Alle drei Aufgaben sind zu diesem Zeitpunkt abgeschlossen.

    return (task1, task2, task3);
}

Beispiel für die Verwendung:

var (catTask, houseTask, teslaTask) = await FeedCatSellHouseBuyCar();

// Alle drei Aufgaben sind zu diesem Zeitpunkt abgeschlossen.

if (catTask.IsCompletedSuccessfully)
    Console.WriteLine($"{catTask.Result.Name} isst ihr gesundes Essen.");
else
    Console.WriteLine("Ihre Katze verhungert!");

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

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

Der try-Block mit dem leeren catch ist erforderlich, weil das .NET 7 immer noch keinen richtigen Weg bietet, um eine Aufgabe ohne Ausnahme im Falle einer Stornierung oder eines Fehlers zu await.

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