Wenn Sie serverseitigen Code haben (d. H. Einige ApiController
) und Ihre Funktionen asynchron sind - also Task<SomeObject>
zurückgeben - wird es als bewährte Methode angesehen, jedes Mal, wenn Sie auf Funktionen warten, die Sie ConfigureAwait(false)
aufrufen?
Ich hatte gelesen, dass es performanter ist, da es nicht zum ursprünglichen Thread-Kontext zurückkehren muss. Wenn jedoch in ASP.NET Web Api Ihre Anforderung 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 Ihres ApiController
zurückgeben Funktion.
Ich habe ein Beispiel für das, worüber ich rede, geschrieben:
public class CustomerController : ApiController
{
public async Task<Customer> Get(int id)
{
// you are on a particular thread here
var customer = await SomeAsyncFunctionThatGetsCustomer(id).ConfigureAwait(false);
// now you are on a different thread! will that cause problems?
return customer;
}
}
Update: ASP.NET Core hat kein SynchronizationContext
. Wenn Sie mit ASP.NET Core arbeiten, spielt es keine Rolle, ob Sie ConfigureAwait(false)
verwenden oder nicht.
Für ASP.NET "Full" oder "Classic" oder was auch immer, der Rest dieser Antwort gilt immer noch.
Originalbeitrag (für Nicht-Core-ASP.NET):
Dieses Video des ASP.NET-Teams enthält die besten Informationen zur Verwendung von async
in ASP.NET.
Ich hatte gelesen, dass es performanter ist, da es nicht zum ursprünglichen Thread-Kontext zurückkehren muss.
Dies gilt für Benutzeroberflächenanwendungen, bei denen nur ein Benutzeroberflächen-Thread "synchronisiert" werden muss.
In ASP.NET ist die Situation etwas komplexer. Wenn eine async
-Methode die Ausführung wieder aufnimmt, wird ein Thread aus dem ASP.NET-Thread-Pool abgerufen. Wenn Sie die Kontexterfassung mit ConfigureAwait(false)
deaktivieren, führt der Thread die Methode direkt weiter aus. Wenn Sie die Kontexterfassung nicht deaktivieren, gibt der Thread den Anforderungskontext erneut ein und setzt die Ausführung der Methode fort.
Daher speichert ConfigureAwait(false)
keinen Thread-Sprung in ASP.NET. Es erspart Ihnen das erneute Eingeben des Anforderungskontexts, aber dies ist normalerweise sehr schnell. ConfigureAwait(false)
könnte nützlich sein, wenn Sie versuchen, eine Anfrage in geringem Umfang parallel zu verarbeiten, TPL jedoch für die meisten dieser Szenarien besser geeignet ist.
Wenn jedoch in ASP.NET Web Api Ihre Anforderung in einem Thread eingeht und Sie auf eine Funktion warten und ConfigureAwait (false) aufrufen, die Sie möglicherweise in einen anderen Thread versetzen kann, wenn Sie das Endergebnis Ihrer ApiController-Funktion zurückgeben .
Tatsächlich kann dies nur durch await
erreicht werden. Sobald Ihre async
-Methode eine await
trifft, wird die -Methode blockiert, aber der -Thread kehrt zum zurück Thread-Pool. Wenn die Methode zum Fortfahren bereit ist, wird ein beliebiger Thread aus dem Thread-Pool abgerufen und zum Fortsetzen der Methode verwendet.
Der einzige Unterschied, den ConfigureAwait
in ASP.NET macht, besteht darin, ob dieser Thread beim Fortsetzen der Methode in den Anforderungskontext eintritt.
Ich habe weitere Hintergrundinformationen in meinem MSDN-Artikel zu SynchronizationContext
und meinem async
Intro-Blog-Beitrag .
Kurze Antwort auf Ihre Frage: Nein. Auf dieser Anwendungsebene sollten Sie ConfigureAwait(false)
nicht aufrufen.
TL; DR-Version der langen Antwort: Wenn Sie eine Bibliothek schreiben, in der Sie Ihren Konsumenten nicht kennen und keinen Synchronisationskontext benötigen (den Sie meiner Meinung nach nicht in einer Bibliothek haben sollten), sollten Sie immer ConfigureAwait(false)
verwenden. Andernfalls kann es zu Deadlocks für die Konsumenten Ihrer Bibliothek kommen, wenn Ihre asynchronen Methoden blockiert werden. Das hängt von der Situation ab.
Hier ist eine etwas ausführlichere Erklärung zur Wichtigkeit der ConfigureAwait
-Methode (ein Zitat aus meinem Blog-Beitrag):
Wenn Sie auf eine Methode mit dem Schlüsselwort await warten, generiert der Compiler eine Reihe von Code für Sie. Mit dieser Aktion wird unter anderem die Synchronisierung mit dem UI-Thread (oder Hauptthread) behandelt. Die Schlüsselkomponente dieser Funktion ist der
SynchronizationContext.Current
, der den Synchronisationskontext für den aktuellen Thread abruft.SynchronizationContext.Current
wird abhängig von der Umgebung, in der Sie sich befinden, ausgefüllt. Die MethodeGetAwaiter
der Task sucht nachSynchronizationContext.Current
. Wenn der aktuelle Synchronisationskontext nicht null ist, wird die Fortsetzung, die an diesen Kellner übergeben wird, in diesen Synchronisationskontext zurückgesendet.Wenn Sie eine Methode blockieren, die die neuen asynchronen Sprachfunktionen verwendet, kommt es zu einem Deadlock, wenn Sie über einen verfügbaren SynchronizationContext verfügen. Wenn Sie solche Methoden blockierend verwenden (indem Sie auf die Task with Wait-Methode warten oder das Ergebnis direkt aus der Result-Eigenschaft der Task entnehmen), blockieren Sie gleichzeitig den Haupt-Thread. Wenn der Task innerhalb dieser Methode im Threadpool abgeschlossen ist, wird die Fortsetzung aufgerufen, um einen Beitrag im Hauptthread zu verfassen, da
SynchronizationContext.Current
verfügbar und erfasst ist. Aber hier gibt es ein Problem: Der UI-Thread ist blockiert und Sie haben einen Deadlock!
Auch hier sind zwei großartige Artikel für Sie, die genau zu Ihrer Frage passen:
Schließlich gibt es ein großartiges kurzes Video von Lucian Wischik zu diesem Thema: Async-Bibliotheksmethoden sollten die Verwendung von Task.ConfigureAwait (false) in Betracht ziehen.
Hoffe das hilft.
Der größte Nachteil, den ich bei der Verwendung von ConfigureAwait (false) festgestellt habe, ist, dass die Thread-Kultur auf die Systemstandardeinstellungen zurückgesetzt wird. Wenn Sie eine Kultur konfiguriert haben, z.
<system.web>
<globalization culture="en-AU" uiCulture="en-AU" />
...
und Sie hosten auf einem Server, dessen Kultur auf en-US eingestellt ist. Dann finden Sie, bevor ConfigureAwait (false) CultureInfo heißt. CurrentCulture gibt en-AU zurück und nachdem Sie en-US erhalten. d.h.
// CultureInfo.CurrentCulture ~ {en-AU}
await xxxx.ConfigureAwait(false);
// CultureInfo.CurrentCulture ~ {en-US}
Wenn Ihre Anwendung etwas ausführt, das eine kulturspezifische Formatierung der Daten erfordert, müssen Sie dies berücksichtigen, wenn Sie ConfigureAwait (false) verwenden.
Ich habe einige allgemeine Gedanken zur Implementierung von Task
:
using
verwenden.ConfigureAwait
wurde in 4.5 eingeführt. Task
wurde in 4.0 eingeführt.Task.ContinueWith
wurde kein Kontextwechsel realisiert ist teuer und standardmäßig deaktiviert.Ich habe ein paar Posts zu diesem Thema, aber meine Meinung ist - zusätzlich zu Tugberks netter Antwort -, dass Sie alle APIs asynchron schalten und im Idealfall den Kontext durchfließ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 keine Wartezeit erfolgt und Sie den Datenfluss aufrechterhalten, sodass 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.