Update: ASP.NET Core hat keineSynchronizationContext
. Wenn Sie mit ASP.NET Core arbeiten, spielt es keine Rolle, ob Sie verwenden ConfigureAwait(false)
oder nicht.
Für ASP.NET "Full" oder "Classic" oder was auch immer gilt der Rest dieser Antwort weiterhin.
Ursprünglicher Beitrag (für Nicht-Core-ASP.NET):
Dieses Video des ASP.NET-Teams enthält die besten Informationen zur Verwendung async
in ASP.NET.
Ich hatte gelesen, dass es leistungsfähiger ist, da es nicht die Thread-Kontexte zurück zum ursprünglichen Thread-Kontext wechseln muss.
Dies gilt für UI-Anwendungen, bei denen es nur einen UI-Thread gibt, mit dem Sie "synchronisieren" müssen.
In ASP.NET ist die Situation etwas komplexer. Wenn eine async
Methode die Ausführung fortsetzt, wird ein Thread aus dem ASP.NET-Thread-Pool abgerufen. Wenn Sie die Kontexterfassung mit deaktivieren ConfigureAwait(false)
, führt der Thread die Methode einfach direkt weiter aus. Wenn Sie die Kontexterfassung nicht deaktivieren, tritt der Thread erneut in den Anforderungskontext ein und führt die Methode weiter aus.
So ConfigureAwait(false)
sparen Sie nicht einen Thread Sprung in ASP.NET; Es erspart Ihnen die erneute Eingabe des Anforderungskontexts, dies ist jedoch normalerweise sehr schnell. ConfigureAwait(false)
Dies kann nützlich sein, wenn Sie versuchen, eine kleine Menge einer Anfrage parallel zu verarbeiten, TPL jedoch für die meisten dieser Szenarien besser geeignet ist.
Bei ASP.NET Web Api jedoch, wenn Ihre Anfrage in einem Thread eingeht und Sie auf eine Funktion warten und ConfigureAwait (false) aufrufen, die Sie möglicherweise in einen anderen Thread versetzen könnte, wenn Sie das Endergebnis Ihrer ApiController-Funktion zurückgeben .
Eigentlich kann man das nur tun await
. Sobald Ihre async
Methode auf trifft await
, wird die Methode blockiert, aber der Thread kehrt zum Thread-Pool zurück. Wenn die Methode zum Fortfahren bereit ist, wird jeder Thread aus dem Thread-Pool entnommen und zum Fortsetzen der Methode verwendet.
Der einzige Unterschied ConfigureAwait
in ASP.NET besteht darin, ob dieser Thread beim Fortsetzen der Methode in den Anforderungskontext wechselt.
Ich habe weitere Hintergrundinformationen in meinem MSDN-ArtikelSynchronizationContext
und meinem async
Intro-Blog-Beitrag .
HttpContext.Current
wird von ASP.NET übertragenSynchronizationContext
, das standardmäßig von Ihnen übertragen wirdawait
, aber nicht vonContinueWith
. OTOH, der Ausführungskontext (einschließlich Sicherheitsbeschränkungen) ist der in CLR über C # erwähnte Kontext und wird von beidenContinueWith
undawait
(auch wenn Sie ihn verwendenConfigureAwait(false)
) übertragen.ConfigureAwait
eigentlich nur dann Sinn macht, wenn Sie auf Aufgaben warten , während esawait
auf alle "erwarteten" Aktionen reagiert. Weitere in Betracht gezogene Optionen waren: Sollte das Standardverhalten den Kontext in einer Bibliothek verwerfen? Oder haben Sie eine Compilereinstellung für das Standardkontextverhalten? Beide wurden abgelehnt, weil es schwieriger ist, nur den Code zu lesen und zu sagen, was er tut.ConfigureAwait(false)
einenResult
/Wait
-basierten Deadlock nicht vermeiden müssen , da Sie unter ASP.NETResult
/ überhaupt nicht verwenden solltenWait
.Kurze Antwort auf Ihre Frage: Nein. Sie sollten nicht
ConfigureAwait(false)
auf Anwendungsebene anrufen .TL; DR-Version der langen Antwort: Wenn Sie eine Bibliothek schreiben, in der Sie Ihren Verbraucher nicht kennen und keinen Synchronisationskontext benötigen (den Sie meiner Meinung nach nicht in einer Bibliothek haben sollten), sollten Sie ihn immer verwenden
ConfigureAwait(false)
. Andernfalls können die Konsumenten Ihrer Bibliothek Deadlocks ausgesetzt sein, indem sie Ihre asynchronen Methoden blockierend nutzen. Dies hängt von der Situation ab.Hier ist eine etwas detailliertere Erklärung zur Wichtigkeit der
ConfigureAwait
Methode (ein Zitat aus meinem Blog-Beitrag):Hier sind auch zwei großartige Artikel für Sie, die genau für Ihre Frage sind:
Schließlich gibt es ein großartiges kurzes Video von Lucian Wischik genau zu diesem Thema: Asynchrone Bibliotheksmethoden sollten die Verwendung von Task.ConfigureAwait (false) in Betracht ziehen .
Hoffe das hilft.
quelle
Task
der Stapel läuft, um das zu bekommenSynchronizationContext
, was falsch ist. DasSynchronizationContext
wird vor dem AufrufTask
des abgerufen und dann wird der Rest des Codes auf demSynchronizationContext
if fortgesetzt, dasSynchronizationContext.Current
nicht null ist.SynchronizationContext.Current
klar ist / oder dass die Bibliothek innerhalb von a aufgerufen wird,Task.Run()
anstatt.ConfigureAwait(false)
über die gesamte Klassenbibliothek schreiben zu müssen ?.ConfigureAwait(false)
s ausdrückt . Vielleicht wäre es für Bibliotheksautoren einfacher, wenn dies das Standardverhalten wäre, aber ich würde annehmen, dass es besser ist, es ein bisschen schwieriger zu machen, eine Bibliothek richtig zu schreiben, als es ein bisschen schwieriger zu machen, eine App richtig zu schreiben.Der größte Nachteil, den ich bei der Verwendung von ConfigureAwait (false) festgestellt habe, ist, dass die Thread-Kultur auf den Systemstandard zurückgesetzt wird. Wenn Sie eine Kultur konfiguriert haben, z.
Wenn Sie auf einem Server hosten, dessen Kultur auf en-US eingestellt ist, werden Sie feststellen, bevor ConfigureAwait (false) CultureInfo heißt. CurrentCulture gibt en-AU zurück und nachdem Sie en-US erhalten. dh
Wenn Ihre Anwendung etwas tut, das eine kulturspezifische Formatierung von Daten erfordert, müssen Sie dies bei der Verwendung von ConfigureAwait (false) berücksichtigen.
quelle
ConfigureAwait(false)
verwendet werden.Ich habe einige allgemeine Gedanken zur Implementierung von
Task
:using
.ConfigureAwait
wurde in 4.5 eingeführt.Task
wurde in 4.0 eingeführt.Task.ContinueWith
nicht b / c wurde erkannt, dass der Kontextwechsel teuer ist und standardmäßig deaktiviert ist.Ich habe ein paar Beiträge zu diesem Thema, aber ich gehe davon aus, dass Sie - zusätzlich zu Tugberks netter Antwort - alle APIs asynchron machen und den Kontext idealerweise fließen lassen sollten. Da Sie asynchron arbeiten, können Sie einfach Fortsetzungen verwenden, anstatt zu warten, sodass kein Deadlock verursacht wird, da in der Bibliothek kein Warten erfolgt und Sie den Fluss beibehalten, damit der Kontext erhalten bleibt (z. B. HttpContext).
Problem ist, wenn eine Bibliothek eine synchrone API verfügbar macht, aber eine andere asynchrone API verwendet - daher müssen Sie
Wait()
/Result
in Ihrem Code verwenden.quelle
Task.Dispose
wenn Sie möchten; Sie brauchen einfach nicht die meiste Zeit. 2)Task
wurde in .NET 4.0 als Teil der TPL eingeführt, die nicht benötigt wurdeConfigureAwait
; Als sieasync
hinzugefügt wurden, verwendeten sie den vorhandenenTask
Typ wieder, anstatt einen neuen zu erfindenFuture
.Task
s; der "Kontext", der von gesteuert wird,ContinueWith
ist einSynchronizationContext
oderTaskScheduler
. Diese unterschiedlichen Kontexte werden im Blog von Stephen Toub ausführlich erläutert .Thread
s floss , aber nicht mehr mitContinueWith()
), macht dies Ihre Antwort verwirrend zu lesen.