wake-up-neo.com

Es wird empfohlen, ConfigureAwait für den gesamten serverseitigen Code aufzurufen

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;
    }
}
487
Arash Emami

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 .

544
Stephen Cleary

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 Methode GetAwaiter der Task sucht nach SynchronizationContext.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.

123
tugberk

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.

16
Mick

Ich habe einige allgemeine Gedanken zur Implementierung von Task:

  1. Aufgabe ist wegwerfbar, aber wir sind sollen nichtusing verwenden.
  2. ConfigureAwait wurde in 4.5 eingeführt. Task wurde in 4.0 eingeführt.
  3. .NET-Threads wurden immer verwendet, um den Kontext zu fließen (siehe C # via CLR-Buch), aber in der Standardimplementierung von Task.ContinueWith wurde kein Kontextwechsel realisiert ist teuer und standardmäßig deaktiviert.
  4. Das Problem ist, dass ein Bibliotheksentwickler sich nicht darum kümmern sollte, ob seine Clients einen Kontextfluss benötigen oder nicht. Daher sollte er nicht entscheiden, ob der Kontext fließen soll oder nicht.
  5. [Später hinzugefügt] Die Tatsache, dass es keine verbindliche Antwort und keinen richtigen Verweis gibt und wir weiterhin darum kämpfen, bedeutet, dass jemand seine Arbeit nicht richtig gemacht hat.

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.

10
Aliostad