C# Language
Contesto di sincronizzazione in attesa asincrona
Ricerca…
Pseudocodice per parole chiave asincrone / attese
Considera un semplice metodo asincrono:
async Task Foo()
{
Bar();
await Baz();
Qux();
}
Semplificando, possiamo dire che questo codice in realtà significa quanto segue:
Task Foo()
{
Bar();
Task t = Baz();
var context = SynchronizationContext.Current;
t.ContinueWith(task) =>
{
if (context == null)
Qux();
else
context.Post((obj) => Qux(), null);
}, TaskScheduler.Current);
return t;
}
Significa che le parole chiave async
/ await
il contesto di sincronizzazione corrente se esiste. Ad esempio, è possibile scrivere codice di libreria che funzioni correttamente in applicazioni UI, Web e Console.
Disabilitare il contesto di sincronizzazione
Per disabilitare il contesto di sincronizzazione, devi chiamare il metodo ConfigureAwait
:
async Task() Foo()
{
await Task.Run(() => Console.WriteLine("Test"));
}
. . .
Foo().ConfigureAwait(false);
ConfigureAwait fornisce un mezzo per evitare il comportamento di cattura predefinito di SynchronizationContext; il passaggio di false per il parametro flowContext impedisce l'utilizzo di SynchronizationContext per riprendere l'esecuzione dopo l'attesa.
Citazione tratta da It's All About the SynchronizationContext .
Perché SynchronizationContext è così importante?
Considera questo esempio:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = RunTooLong();
}
Questo metodo bloccherà l'applicazione dell'interfaccia utente fino al completamento di RunTooLong
. L'applicazione non risponderà.
Puoi provare a eseguire il codice interno in modo asincrono:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => label1.Text = RunTooLong());
}
Ma questo codice non verrà eseguito perché il corpo interno può essere eseguito su thread non UI e non dovrebbe modificare direttamente le proprietà dell'interfaccia utente :
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
if (label1.InvokeRequired)
lable1.BeginInvoke((Action) delegate() { label1.Text = label1Text; });
else
label1.Text = label1Text;
});
}
Ora non dimenticare di usare sempre questo modello. Oppure prova SynchronizationContext.Post
che lo farà per te:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
SynchronizationContext.Current.Post((obj) =>
{
label1.Text = label1 Text);
}, null);
});
}