C# Language
Synchronisierungskontext in Async-Await
Suche…
Pseudocode für async / await-Schlüsselwörter
Betrachten Sie eine einfache asynchrone Methode:
async Task Foo()
{
Bar();
await Baz();
Qux();
}
Vereinfachend können wir sagen, dass dieser Code eigentlich Folgendes bedeutet:
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;
}
Das bedeutet, dass async
/ await
Schlüsselwörter den aktuellen Synchronisationskontext verwenden, sofern vorhanden. Sie können also Bibliothekscode schreiben, der in UI-, Web- und Konsolenanwendungen ordnungsgemäß funktionieren würde.
Synchronisierungskontext deaktivieren
Um den Synchronisationskontext zu deaktivieren, rufen Sie die ConfigureAwait
Methode auf:
async Task() Foo()
{
await Task.Run(() => Console.WriteLine("Test"));
}
. . .
Foo().ConfigureAwait(false);
Mit ConfigureAwait können Sie das standardmäßige Aufnahmeverhalten von SynchronizationContext vermeiden. Durch das Übergeben von false für den Parameter flowContext wird verhindert, dass SynchronizationContext verwendet wird, um die Ausführung nach dem Erwarten wieder aufzunehmen.
Zitat von Es ist alles über den SynchronizationContext .
Warum ist SynchronizationContext so wichtig?
Betrachten Sie dieses Beispiel:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = RunTooLong();
}
Diese Methode friert die UI-Anwendung ein, bis RunTooLong
abgeschlossen ist. Die Anwendung reagiert nicht.
Sie können versuchen, den inneren Code asynchron auszuführen:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => label1.Text = RunTooLong());
}
Dieser Code wird jedoch nicht ausgeführt, da der innere Körper möglicherweise auf einem Thread außerhalb der Benutzeroberfläche ausgeführt wird und die Eigenschaften der Benutzeroberfläche nicht direkt geändert werden sollten :
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;
});
}
Vergessen Sie nicht, immer dieses Muster zu verwenden. Oder versuchen Sie es mit SynchronizationContext.Post
, das es für Sie machen wird:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
SynchronizationContext.Current.Post((obj) =>
{
label1.Text = label1 Text);
}, null);
});
}