In diesem Code:
private async void button1_Click(object sender, EventArgs e) {
try {
await Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2());
}
catch (Exception ex) {
// Expect AggregateException, but got InvalidTimeZoneException
}
}
Task DoLongThingAsyncEx1() {
return Task.Run(() => { throw new InvalidTimeZoneException(); });
}
Task DoLongThingAsyncEx2() {
return Task.Run(() => { throw new InvalidOperation();});
}
Ich hatte erwartet, eine WhenAll
zu erstellen und zu werfen AggregateException
, da mindestens eine der Aufgaben, auf die es wartete, eine Ausnahme auslöste. Stattdessen erhalte ich eine einzelne Ausnahme, die von einer der Aufgaben ausgelöst wurde.
Erstellt WhenAll
nicht immer eine AggregateException
?
.net
exception
asynchronous
tap
Michael Ray Lovett
quelle
quelle
AggregateException
. Wenn SieTask.Wait
anstelle vonawait
in Ihrem Beispiel verwendet, würden Sie fangenAggregateException
Task.WhenAll
und bin in dieselbe Falle geraten. Also habe ich versucht , auf dieses Verhalten einzugehen.Antworten:
Ich weiß nicht genau, wo, aber ich habe irgendwo gelesen, dass sie mit neuen asynchronen / wartenden Schlüsselwörtern die
AggregateException
in die eigentliche Ausnahme auspacken .Im catch-Block erhalten Sie also die eigentliche Ausnahme und nicht die aggregierte. Dies hilft uns, natürlicheren und intuitiveren Code zu schreiben.
Dies war auch erforderlich, um die Konvertierung von vorhandenem Code in die Verwendung von async / await zu vereinfachen, wenn viele Codes bestimmte Ausnahmen und keine aggregierten Ausnahmen erwarten .
- Bearbeiten -
Verstanden:
Eine asynchrone Grundierung von Bill Wagner
quelle
Ich weiß, dass dies eine Frage ist, die bereits beantwortet wurde, aber die gewählte Antwort löst das Problem des OP nicht wirklich. Deshalb dachte ich, ich würde dies posten.
Diese Lösung gibt Ihnen die aggregierte Ausnahme (dh alle Ausnahmen, die von den verschiedenen Aufgaben ausgelöst wurden) und blockiert nicht (der Workflow ist immer noch asynchron).
Der Schlüssel besteht darin, einen Verweis auf die Aggregataufgabe zu speichern, bevor Sie darauf warten. Anschließend können Sie auf die Exception-Eigenschaft zugreifen, die Ihre AggregateException enthält (auch wenn nur eine Task eine Ausnahme ausgelöst hat).
Hoffe das ist noch nützlich. Ich weiß, dass ich heute dieses Problem hatte.
quelle
throw task.Exception;
in dencatch
Block stecken? (Es verwirrt mich, einen leeren Fang zu sehen, wenn Ausnahmen tatsächlich behandelt werden.)Task.IsCanceled
) nicht ordnungsgemäß weitergegeben wird. Dies kann löst die Verwendung eines Verlängerungs Helfer wie sein diese .Sie können alle Aufgaben durchlaufen, um festzustellen, ob mehrere eine Ausnahme ausgelöst haben:
quelle
WhenAll
wird bei der ersten Ausnahme beendet und gibt diese zurück. siehe: stackoverflow.com/questions/6123406/waitall-vs-whenallexceptions
enthält beide Ausnahmen.await
dass die erste Ausnahme dadurch entpackt wird, aber alle Ausnahmen sind tatsächlich noch über das Array von Aufgaben verfügbar.Ich dachte nur, ich würde die Antwort von @ Richiban erweitern, um zu sagen, dass Sie die AggregateException auch im catch-Block behandeln können, indem Sie sie aus der Aufgabe heraus referenzieren. Z.B:
quelle
Du denkst an
Task.WaitAll
- es wirft einAggregateException
.WhenAll löst nur die erste Ausnahme der Liste der Ausnahmen aus, auf die es stößt.
quelle
WhenAll
Methode zurückgegebene Aufgabe verfügt über eineException
Eigenschaft,AggregateException
die alle in ihr ausgelösten Ausnahmen enthältInnerExceptions
. Was hier passiert ist, dassawait
die erste innere Ausnahme anstelle derAggregateException
selbst ausgelöst wird (wie der Decyklon sagte). Wenn Sie dieWait
Methode der Aufgabe aufrufen, anstatt darauf zu warten, wird die ursprüngliche Ausnahme ausgelöst.Viele gute Antworten hier, aber ich möchte trotzdem meine Beschimpfungen veröffentlichen, da ich gerade auf dasselbe Problem gestoßen bin und einige Nachforschungen angestellt habe. Oder springen Sie zur folgenden TLDR-Version.
Das Problem
Das Warten auf die
task
Rückgabe durch löstTask.WhenAll
nur die erste Ausnahme derAggregateException
gespeichertentask.Exception
Daten aus, selbst wenn mehrere Aufgaben fehlerhaft sind.Die aktuellen Dokumente zum Beispiel
Task.WhenAll
:Das ist richtig, sagt aber nichts über das oben erwähnte "Auspacken" -Verhalten aus, wenn die zurückgegebene Aufgabe erwartet wird.
Ich nehme an, die Dokumente erwähnen es nicht, weil dieses Verhalten nicht spezifisch ist
Task.WhenAll
.Es ist einfach
Task.Exception
vom TypAggregateException
und fürawait
Fortsetzungen wird es immer als erste innere Ausnahme von Natur aus ausgepackt. Dies ist in den meisten Fällen großartig, da es normalerweiseTask.Exception
nur eine innere Ausnahme gibt. Beachten Sie jedoch diesen Code:Hier wird eine Instanz von genau so
AggregateException
auf ihre erste innere Ausnahme entpackt,InvalidOperationException
wie wir es vielleicht hattenTask.WhenAll
. Wir hätten es nicht beobachten können,DivideByZeroException
wenn wir nichttask.Exception.InnerExceptions
direkt durchgegangen wären .Stephen Toub von Microsoft erklärt den Grund für dieses Verhalten im zugehörigen GitHub-Problem :
Eine andere wichtige Sache zu beachten, ist dieses Auspackverhalten flach. Das heißt, es wird nur die erste Ausnahme auspacken
AggregateException.InnerExceptions
und dort belassen , selbst wenn es sich zufällig um eine Instanz einer anderen handeltAggregateException
. Dies kann noch eine weitere Verwirrungsebene hinzufügen. Ändern wir zum Beispiel FolgendesWhenAllWrong
:Eine Lösung (TLDR)
Zurück zu
await Task.WhenAll(...)
, was ich persönlich wollte, ist in der Lage zu sein:AggregateException
wenn mehr als eine Ausnahme von einer oder mehreren Aufgaben gemeinsam ausgelöst wurde.Task
einzige zu speichern, um es zu überprüfenTask.Exception
.Task.IsCanceled
), da so etwas das nicht tun würde :Task t = Task.WhenAll(...); try { await t; } catch { throw t.Exception; }
.Dafür habe ich folgende Erweiterung zusammengestellt:
Nun funktioniert Folgendes so, wie ich es möchte:
quelle
Das funktioniert bei mir
quelle
WhenAll
ist nicht dasselbe wieWhenAny
.await Task.WhenAny(tasks)
wird abgeschlossen, sobald eine Aufgabe abgeschlossen ist. Wenn Sie also eine Aufgabe haben, die sofort abgeschlossen wird und erfolgreich ist, und eine andere einige Sekunden dauert, bevor eine Ausnahme ausgelöst wird, wird diese sofort ohne Fehler zurückgegeben.In Ihrem Code wird die erste Ausnahme vom Design zurückgegeben, wie unter http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5 erläutert . aspx
Für Ihre Frage erhalten Sie die AggreateException, wenn Sie Code wie folgt schreiben:
quelle