2013-02-07 2 views
5

Ich habe async in WebForms getestet und ausnahmsweise geht es bei meiner Frage nicht darum, wie etwas zu tun ist, sondern darum, wie etwas, das bereits funktioniert, funktioniert. Das ist mein Testcode:Async/Warten auf WebForms - Wie läuft die Fortsetzung ab, bevor der Seitenlebenszyklus endet?

protected override void OnPreRender(EventArgs e) 
{ 
    Response.Write("OnPreRender<Br>"); 
} 
protected override void OnPreRenderComplete(EventArgs e) 
{ 
    Response.Write("OnPreRenderComplete<Br>"); 
} 
protected async override void OnLoadComplete(EventArgs e) 
{ 
    Response.Write("OnLoadComplete<br>"); 
    var t1 = Task.Factory.StartNew(() => { 
     System.Threading.Thread.Sleep(2000); 
     return 1; 
    }); 

    //This actually does run: 
    Response.Write((await t1).ToString()); 
} 

Also meine Aufgabe pausiert für ein bisschen und dann schreibt das Ergebnis aus. Meine Frage ist - ich würde nicht erwarten, dass dies funktioniert, weil Kontrolle von der OnLoadComplete-Methode erhalten wurde - Ich würde erwarten, dass die Seite das Rendern tatsächlich beendet und an den Client zurückgegeben wird, bevor meine Aufgabe jemals zurückgegeben wird.

Die tatsächliche Ausgabe lautet:

OnLoadComplete 
OnPreRender 
1OnPreRenderComplete 

es ist also klar, dass die OnLoadComplete Methode Kontrolle ergab, so dass OnPreRender laufen konnte und dann wieder die Steuerung an die OnLoadComplete. Mein erwartetes Ergebnis war, dass die "1" nie drucken würde, da die nachfolgenden Ereignisse ausgelöst würden und der Thread der Seite entweder gelöscht würde oder der Post-Task-Schreibvorgang nach dem Senden der Antwort stattfände. Ich denke, angesichts der obigen Ausführungen ist es nicht verwunderlich, dass, selbst wenn ich 10 Sekunden lang verzögere, das Ergebnis genau das gleiche ist.

Ich nehme an, es gibt eine Verkabelung in der WebForm-Engine, die sicherstellt, dass alle Wartezeiten abgeschlossen sind, bevor die nächste Phase des Lebenszyklus der Seite fortgesetzt wird. Weiß jemand genau wie das passiert? Ich habe Angst, async/await in Methoden zu verwenden, die vor anderen Ereignissen abgeschlossen werden müssen, aus Angst, dass die Fortsetzung zu spät sein wird, aber wenn es intern gehandhabt wird, dann mache ich mir keine Sorgen.

Antwort

11

Für ASP.NET sollten Sie nur async Methoden auf .NET 4.5 verwenden. Ich werde am Ende erklären, warum.

Ich habe an article on SynchronizationContext, die hilft, die Lücken zu füllen, wie dies auf ASP.NET funktioniert. Beachten Sie, dass ASP.NET vor langer Zeit asynchrone Operationen unterstützt hat (.NET 2.0 IIRC). Sie könnten asynchrone Vorgänge auf verschiedene Arten registrieren, aber für diese Beschreibung konzentrieren wir uns auf SynchronizationContext.OperationStarted.

ASP.NET erstellt eine SynchronizationContext für jede Anfrage, und es weiß, dass die Anfrage nicht abgeschlossen ist, bis alle registrierten Operationen abgeschlossen sind (durch Aufruf SynchronizationContext.OperationCompleted). Event-based asynchronous pattern components (wie BackgroundWorker) benachrichtigt die SynchronizationContext automatisch, wenn sie gestartet und abgeschlossen werden.

In ähnlicher Weise, async void Methoden (die neue task-based asynchronous pattern) benachrichtigt die SynchronizationContext automatisch, wenn sie starten und abschließen. Wenn Sie also OnLoadComplete als async void-Methode überschreiben, fügt der Compiler Code für Sie ein, der OperationStarted am Anfang und OperationCompleted aufruft, wenn es abgeschlossen ist.

So weit, so gut - ASP.NET weiß jetzt, die Anfrage lebendig zu halten, bis alle asynchronen Operationen für diese Anfrage abgeschlossen sind. Dies gilt auch dann, wenn keine Threads die Anfrage bearbeiten.

Nun der Vorbehalt: ASP.NET vor .NET 4.5 würde dies bei einer Anfrage Ebene behandeln. In ASP.NET 4.5 wurde die Lebenszyklus-Pipeline intelligenter gestaltet, sodass der Seitenlebenszyklus verzögert wurde, bis die asynchronen Vorgänge abgeschlossen waren. Mit dem alten ASP.NET würden die "Pre" -Handler starten an diesem Punkt in der Pipeline, aber möglicherweise nicht bis später fertig. Das neue ASP.NET verzögert den Rest der Seitenausführung, um sicherzustellen, dass async Handler abgeschlossen sind, bevor Sie im Lebenszyklus fortfahren.

Außerdem erkennt ASP.NET 4.5, ob Sie einen async-Handler verwendet haben, wo es keinen geben sollte, und Sie über den Fehler benachrichtigen.