C# Language
Synkroniseringskontext i Async-Await
Sök…
Pseudocode för nyckelord / vänta på nyckelord
Tänk på en enkel asynkron metod:
async Task Foo()
{
Bar();
await Baz();
Qux();
}
Förenkling kan vi säga att den här koden faktiskt betyder följande:
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;
}
Det betyder att async
som är async
/ await
använder aktuell synkroniseringskontext om de finns. Du kan skriva bibliotekskod som fungerar korrekt i UI-, webb- och konsolapplikationer.
Inaktiverar synkroniseringskontext
För att inaktivera synkroniseringskontext måste du anropa metoden ConfigureAwait
:
async Task() Foo()
{
await Task.Run(() => Console.WriteLine("Test"));
}
. . .
Foo().ConfigureAwait(false);
ConfigureAwait tillhandahåller ett sätt att undvika standarduppförandet av SynchronizationContext; att passera falskt för parametern flowContext förhindrar att SynchronizationContext används för att återuppta körningen efter väntan.
Citat från It's All About SynchronizationContext .
Varför SynchronizationContext är så viktigt?
Tänk på detta exempel:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = RunTooLong();
}
Den här metoden fryser UI-applikationen tills RunTooLong
är klar. Ansökan kommer inte att svara.
Du kan prova att köra inre kod asynkront:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => label1.Text = RunTooLong());
}
Men den här koden kommer inte att köras eftersom innerkroppen kan köras på tråd utan UI och den bör inte ändra UI-egenskaper direkt :
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;
});
}
Glöm inte alltid att använda detta mönster. Eller prova SynchronizationContext.Post
som gör det åt dig:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
SynchronizationContext.Current.Post((obj) =>
{
label1.Text = label1 Text);
}, null);
});
}