2015-01-08 4 views
37

Betrachten Sie den folgenden Code von Windows Forms:Warum würde ich Mühe haben, Task.ConfigureAwait (continueOnCapturedContext: false) zu verwenden;

private async void UpdateUIControlClicked(object sender, EventArgs e) 
    { 
     this.txtUIControl.Text = "I will be updated after 2nd await - i hope!"; 
     await Task.Delay(5000).ConfigureAwait(continueOnCapturedContext: false); 
     this.txtUIControl.Text = "I am updated now."; 
    } 

Hier ist die Ausnahme in der 3. Zeile ausgelöst wird, weil, nachdem der Code wartet auf nicht-UI-Thread ausgeführt wird. Wo ist ConfigureAwait (false) nützlich?

+0

Okay - in nicht UI-Umgebung spielt es keine Rolle, welcher Thread die Ausführung des Rests der Methode fortsetzt. Wird bei Verwendung von false ein Leistungseinbruch erzielt? Kann warten, muss mehr Arbeit (SynchronizationContext), wenn es gezwungen ist, die Fortsetzung auf dem ursprünglichen Thread auszuführen. –

+1

Lesen Sie [this] (http://stackoverflow.com/questions/13489065/best-practice-to-call-configureawait-for-all-server-side-code) Post. Hat großes Detail –

Antwort

49

Stephen Cleary has a really good series on this you can find here, ich das Stück speziell auf Ihre Frage zitiert:

Die meiste Zeit, nicht wahr Notwendigkeit zurück in die „main“ Kontext zu synchronisieren. Die meisten asynchronen Methoden werden im Hinblick auf die Zusammensetzung entworfen: Sie warten auf andere Operationen und jeder stellt eine asynchrone Operation selbst dar (die von anderen zusammengesetzt werden kann). In diesem Fall sollten Sie die Erwartenden zu nicht Erfassung durch den aktuellen Kontext und false vorbei ConfigureAwait Aufruf sagen, zum Beispiel:

private async Task DownloadFileAsync(string fileName) 
{ 
    // Use HttpClient or whatever to download the file contents. 
    var fileContents = await DownloadFileContentsAsync(fileName).ConfigureAwait(false); 

    // Note that because of the ConfigureAwait(false), we are not on the original context here. 
    // Instead, we're running on the thread pool. 

    // Write the file contents out to a disk file. 
    await WriteToDiskAsync(fileName, fileContents).ConfigureAwait(false); 

    // The second call to ConfigureAwait(false) is not *required*, but it is Good Practice. 
} 

// WinForms example (it works exactly the same for WPF). 
private async void DownloadFileButton_Click(object sender, EventArgs e) 
{ 
    // Since we asynchronously wait, the UI thread is not blocked by the file download. 
    await DownloadFileAsync(fileNameTextBox.Text); 

    // Since we resume on the UI context, we can directly access UI elements. 
    resultTextBox.Text = "File downloaded!"; 
} 

Das Wichtigste bei diesem Beispiel zu beachten ist, dass jede „Ebene“ asynchrone Methodenaufrufe haben ihren eigenen Kontext. DownloadFileButton_Click wurde im UI-Kontext gestartet und DownloadFileAsync aufgerufen. DownloadFileAsync wurde auch im UI-Kontext gestartet, trat dann jedoch durch Aufruf von ConfigureAwait(false) aus dem Kontext heraus. Der Rest von DownloadFileAsync wird im Thread-Pool-Kontext ausgeführt. Wenn jedoch DownloadFileAsync abgeschlossen und DownloadFileButton _Click fortgesetzt wird, wird im UI-Kontext fortgesetzt.

Eine gute Faustregel ist, ConfigureAwait(false) zu verwenden, wenn Sie Sie kennen tun brauchen den Kontext.

+1

Danke Victor Learned. Es ist klar, dass, wenn wir wissen, dass der Code nach dem Warten auf jeden Thread ausgeführt werden kann, ConfigureAwait (continueOnCapturedContext: false); So etwas wie ein anderer Webservice/IO-Vorgang wird benötigt, der in jedem anderen Thread als der UI ausgeführt werden kann. Da wir in einem anderen Thread als UI sind, sollten wir zur Aktualisierung der UI-Steuerung die Methode Control.BeginInvoke()/Invoke verwenden? –

+29

Wenn es empfohlen wird, 'ConfigureAwait (false)' zu verwenden, warum ist es dann nicht standardmäßig auf false? –

+1

@JohnC, weil es (in einigen sehr speziellen Fällen) zu Fehlern führen kann - im Gegensatz zu ConfigureAwait (true), das immer funktioniert. Fühlen Sie sich frei, die verfügbaren Fody Addins zu überprüfen, die automatisch alle Warter in Ihrem Code ändern (also den Standard ändern). – Snicker

-11

Wenn ConfigureAwait zu false initialisiert wird, wird der Code nicht auf dem Haupt-Thread (UI-Thread) laufen, aber wenn es true ist, dann wird es.

Ich empfehle Ihnen, häufiger SyncContext zu diesem Thema zu verwenden.

+3

Der Aufruf von '.ConfigureAwait (true)' auf einem Nicht-UI-Thread bewirkt nicht, dass der Code den UI-Thread ausführt. Wenn Sie einen SyncContext verwenden, wird er diesen Kontext für die Fortsetzung verwenden. Wenn Sie sich nicht in einem Kontext befinden oder false übergeben, wird der Standardkontext verwendet, bei dem es sich um den Threadpool handelt. –

Verwandte Themen