2015-01-08 3 views
37

Si consideri il seguente codice di Windows Form:Perché dovrei preoccuparmi di usare Task.ConfigureAwait (continueOnCapturedContext: false);

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."; 
    } 

Qui l'eccezione viene generata al 3 ° linea perché dopo attendono il codice viene eseguito sul filetto non-UI. Dove ConfigureAwait (false) è utile?

+0

OK - in ambiente non UI, non importa quale thread continua l'esecuzione del resto del metodo. C'è qualche impatto sulle prestazioni quando si usa il falso? Può essere atteso deve fare più lavoro (SynchronisationContext) se è costretto a eseguire la continuazione sul thread originale. –

+1

Leggere [questo] (http://stackoverflow.com/questions/13489065/best-practice-to-call-configureawait-for-all-server-side-code) post. Ha un grande dettaglio –

risposta

49

Stephen Cleary has a really good series on this you can find here, ho citato il pezzo specifico alla tua domanda:

maggior parte del tempo, non è necessità per sincronizzare di nuovo al contesto “principale”. La maggior parte dei metodi asincroni verranno progettati tenendo conto della composizione: attendono altre operazioni e ognuna rappresenta un'operazione stessa asincrona (che può essere composta da altri). In questo caso, si vuole dire al awaiter a non cattura contesto corrente chiamando ConfigureAwait e passando false, ad esempio:

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!"; 
} 

La cosa importante da notare con questo esempio è che ogni “livello” delle chiamate al metodo asincrono ha il suo contesto. DownloadFileButton_Click avviato nel contesto dell'interfaccia utente e denominato DownloadFileAsync. DownloadFileAsync avviato anche nel contesto dell'interfaccia utente, ma poi uscito dal suo contesto chiamando ConfigureAwait(false). Il resto di DownloadFileAsync viene eseguito nel contesto del pool di thread. Tuttavia, quando DownloadFileAsync completa e DownloadFileButton _Click riprende, lo riprende nel contesto UI.

Una buona regola empirica è quella di utilizzare ConfigureAwait(false) a meno che non si conosca lo do necessario il contesto.

+1

Grazie Victor Learned. E 'chiaro che quando sappiamo che il codice dopo l'attesa può essere eseguito su qualsiasi thread, possiamo usare ConfigureAwait (continueOnCapturedContext: false); È necessario qualcosa come un altro servizio web/operazione IO che può essere eseguita su qualsiasi thread diverso dall'interfaccia utente. Dato che siamo in un altro thread rispetto all'interfaccia utente, per aggiornare il controllo dell'interfaccia utente dovremmo utilizzare il metodo Control.BeginInvoke()/Invoke? –

+29

Se si consiglia di usare 'ConfigureAwait (false)', allora perché non è già impostato su false? –

+1

@JohnC perché potrebbe produrre errori (in alcuni casi molto speciali) - a differenza di ConfigureAwait (true) che funzionerà tutte le volte. Sentiti libero di controllare gli addin di fody disponibili che cambiano automaticamente tutti gli attenditori nel tuo codice (quindi cambia il valore predefinito). – Snicker

-11

Se ConfigureAwait è inizializzato a false, il codice non verrà eseguito sul thread principale (thread UI), ma se è true, allora sarà.

Vi consiglio di utilizzare SyncContext più spesso su questo.

+3

Chiamare '.ConfigureAwait (true)' su un thread non dell'interfaccia utente non fa in modo che il codice esegua il thread dell'interfaccia utente. Quello che fa è se tu stai su un SyncContext che userà quel contesto per la continuazione, se non sei in un contesto o non passi in falso userà il contesto predefinito che è il threadpool. –