C# Language
Kontekst synchronizacji w Async-Await
Szukaj…
Pseudokod dla słów kluczowych async / czekaj
Rozważ prostą metodę asynchroniczną:
async Task Foo()
{
Bar();
await Baz();
Qux();
}
Upraszczając, możemy powiedzieć, że ten kod w rzeczywistości oznacza:
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;
}
Oznacza to, że słowa kluczowe async
/ await
wykorzystują bieżący kontekst synchronizacji, jeśli istnieje. Można np. Napisać kod biblioteki, który działałby poprawnie w aplikacjach UI, Web i Console.
Wyłączanie kontekstu synchronizacji
Aby wyłączyć kontekst synchronizacji, należy wywołać metodę ConfigureAwait
:
async Task() Foo()
{
await Task.Run(() => Console.WriteLine("Test"));
}
. . .
Foo().ConfigureAwait(false);
ConfigureAwait zapewnia sposób na uniknięcie domyślnego zachowania przechwytywania SynchronizationContext; przekazanie wartości false dla parametru flowContext uniemożliwia użycie SynchronizationContext do wznowienia wykonywania po oczekiwaniu.
Cytat z „Wszystko o kontekście synchronizacji” .
Dlaczego SynchronizationContext jest tak ważny?
Rozważ ten przykład:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = RunTooLong();
}
Ta metoda spowoduje zawieszenie aplikacji interfejsu użytkownika do czasu zakończenia RunTooLong
. Aplikacja nie będzie odpowiadać.
Możesz spróbować uruchomić wewnętrzny kod asynchronicznie:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => label1.Text = RunTooLong());
}
Ale ten kod nie zostanie wykonany, ponieważ wewnętrzna treść może być uruchamiana w wątku innym niż interfejs użytkownika i nie powinna bezpośrednio zmieniać właściwości interfejsu :
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;
});
}
Teraz nie zapomnij zawsze używać tego wzoru. Lub spróbuj SynchronizationContext.Post
, który zrobi to za Ciebie:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
SynchronizationContext.Current.Post((obj) =>
{
label1.Text = label1 Text);
}, null);
});
}