2016-05-28 11 views
0

Es scheint, dass ein asynchrones Verfahren seine Ausnahmen in eine Task, dass die Methode zurückgibt, einschließlich der immer fängt die erste await“vor geworfen Mit anderen Worten: der folgende Code funktioniert wie beabsichtigt und keine Ausnahmen, bis die Zeile mit Task.WhenAll geworfen.:Wird asynchrone Task-Methode nie geworfen?

async Task DoSomethingAsync(int i) 
{ 
     if (i == 2) throw new InvalidOperationException(); 
     await Task.Delay(1000); 
} 

... 

var tasks = new List<Task>(); 
for(int i = 0; i < 3; ++i) 
{ 
    var t = DoSomethingAsync(i); // no awaits here 
    tasks.Add(t); 
} 


// Wait for all tasks 
await Task.WhenAll(tasks); // throws InvalidOperation when the other 2 tasks succeed 

die Frage:

ist das Verhalten von Asynchron-Methoden Teil der Sprache spec oder es ist nur so, wie es in der aktuellen Version von .NET implementiert Kann ich vertrauen auf diesem Verhalten in meinen? Code?

+1

In Zusätzlich zu Jon Skeets Antwort unten spricht Stephen Cleary über dieses Verhalten kurz in diesem [Artikel] (http://blog.stephencleary.com/2014/10/a-tour-of-task-part-5-wait.html) – Nasreddine

Antwort

4

Ist das Verhalten von Asynchron-Methoden Teil der Sprache spec

Ja, es ist. Aus dem Abschnitt 10.15.1 der C# 5-Spezifikation:

  • Wenn der Funktionskörper als Ergebnis einer abgefangene Ausnahme beendet (§8.9.5) die Ausnahme wird in dem Rücklauf Aufgabe aufgezeichnet, die in eine gestellt wird fehlerhafter Zustand.

Abschnitt 10.15.2 enthält Einzelheiten über async Methoden void Rückkehr:

Wenn der Rückgabetyp der Asynchron-Funktion void ist, die Bewertung der oben unterscheidet sich in folgenden Weise: Weil keine Aufgabe zurückgegeben wird, kommuniziert die Funktion stattdessen die Beendigung und Ausnahmen des Synchronisationskontexts des aktuellen Threads. Die genaue Definition des Synchronisationskontexts ist implementierungsabhängig, ist jedoch eine Repräsentation von "wo" der aktuelle Thread ausgeführt wird. Der Synchronisationskontext wird benachrichtigt, wenn die Auswertung einer Async-Funktion, die Leerstellen zurückgibt, beginnt, erfolgreich abgeschlossen wird oder bewirkt, dass eine nicht abgefangene Ausnahme ausgelöst wird.

Zwar glaube ich nicht, es ausdrücklich sagt, dass der Anruf nicht selbst wirft, aber ich glaube, dass die beabsichtigte Interpretation der Spezifikation ist.

Wenn Sie eine Methode wollen zu werfen, wenn (beispielsweise) Voraussetzungen nicht erfüllt sind, aber dann normale Async Verhalten verwenden, können Sie den gleichen Ansatz wie für Iterator Blöcke gemeinsam ist:

public Task Foo(int someParameter) 
{ 
    // Check preconditions here and throw - note that this isn't an 
    // async method, so the exceptions will be thrown synchronously. 
    return FooImpl(someParameter); 
} 

private async Task FooImpl(int someParameter) 
{ 
    // Assume everything is valid now. 
    // Normal async method implementation. 
} 
+0

Danke, Jon! Dieses Verhalten mit Hilfe eines Wrappers zu isolieren, ist genau das, was ich an einigen Stellen zu tun hatte, als ich alten Code, der vor .NET 4.5 geschrieben wurde, refactorierte. – alexm