2017-11-10 7 views
3

Versuchen zu verstehen, wann ich ConfigureAwait() verwenden sollte. Accordin zum Buch:Grundlegendes ConfigureAwait

When an async method resumes after an await, by default it will resume executing within the same context. This can cause performance problems if that context was a UI context and a large number of async methods are resuming on the UI context. 
    Solution 
To avoid resuming on a context, await the result of ConfigureAwait and pass false 
for its continueOnCapturedContext parameter: 

async Task ResumeWithoutContextAsync() 
{ 
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false); 

// This method discards its context when it resumes. 
} 

Was Kontext ist und wie ConfigureAwait zu sehen() ändert die Dinge in Beispielanwendung:

static async Task ResumeWithoutContextAsync() 
    { 
     await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(true); 
     Console.WriteLine("ManagedThreadId {0}", Thread.CurrentThread.ManagedThreadId); 

     // This method discards its context when it resumes. 
    } 

    static void Main(string[] args) 
    { 

     ResumeWithoutContextAsync(); 

     Console.ReadLine(); 

    } 

Ich dachte, dass Kontext Thema ist, aber es ist nicht.

+0

Es gibt einen Begriff namens "Blockierung". Einige Methoden kehren zurück, wenn die Arbeit synchron beendet wird (Blockierung) und andere sind asynchron (nicht blockierend) und kehren zurück, bevor alle Arbeiten abgeschlossen sind. Warten ist erforderlich, wenn eine Methode nicht blockiert ist und Sie warten müssen, bis alle Arbeiten abgeschlossen sind. – jdweng

+0

https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – peco

Antwort

1

Kontext hier ist SynchronizationContext. Warten wird eine Fortsetzung (der Rest der Methode nach dem Warten) in den aktuellen Kontext (SynchronizationContext.Current) posten, wenn sie vorhanden ist und Sie ConfigureAwait false nicht verwendet haben. Wenn die Continuation nicht im Kontext steht, wird sie im Thread-Pool-Thread ausgeführt. In Konsolenanwendungen gibt es standardmäßig keinen Synchronisierungskontext, so dass ConfigureAwait in Ihrem Test keine Auswirkung hat. Sie können Dummy-Kontext erstellen, obwohl die Wirkung zu sehen:

class MySynchornizationContext : SynchronizationContext { 
    public override void Post(SendOrPostCallback d, object state) { 
     Console.WriteLine("posted"); 
     base.Post(d, state); 
    } 

    public override void Send(SendOrPostCallback d, object state) { 
     Console.WriteLine("sent"); 
     base.Send(d, state); 
    } 
} 

Dann am Anfang Main Methode:

SynchronizationContext.SetSynchronizationContext(new MySynchornizationContext()); 

Mit ConfigureAwait(true) (oder ohne überhaupt) - Sie, dass die Fortsetzung Veröffentlichungs- zu sehen war Kontext ("gepostete" Zeile in der Konsole). Mit ConfigureAwait(false) - Sie werden sehen, ist es nicht.

Reale Synchronisationskontexte sind komplizierter als das natürlich. Ein UI-Kontext (z. B. in Winforms oder WPF) "reiht" Fortsetzungen in die Warteschlange und führt sie auf einem (UI) -Thread aus. Das kann problematisch sein, wie in Ihrem Frage-Zitat beschrieben, aus verschiedenen Gründen (und kann zu Deadlocks führen). Wenn Sie also eine allgemeine Bibliothek schreiben, ist es vorteilhaft, ConfigureAwait(false zu verwenden, um dieses Verhalten zu vermeiden.

SynchronizationContext ist nicht erforderlich, um alle Rückrufe zu einem einzigen Thread natürlich zu buchen, kann es alles mit ihnen tun. Zum Beispiel wird der ASP.NET MVC-Kontext (zumindest alte Versionen) Rückrufe an den Anfrage-Thread senden, und es können viele Anfragen so viele Anfrage-Threads geben.