C# Language
Synchronisatiecontext in Async-Await
Zoeken…
Pseudocode voor async / wacht op trefwoorden
Overweeg een eenvoudige asynchrone methode:
async Task Foo()
{
Bar();
await Baz();
Qux();
}
Vereenvoudigend kunnen we zeggen dat deze code eigenlijk het volgende betekent:
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;
}
Het betekent dat async
/ await
sleutelwoorden de huidige synchronisatiecontext gebruiken als deze bestaat. Dat wil zeggen dat u bibliotheekcode kunt schrijven die correct zou werken in UI-, web- en consoletoepassingen.
Synchronisatiecontext uitschakelen
Om de synchronisatiecontext uit te schakelen, moet u de methode ConfigureAwait
aanroepen:
async Task() Foo()
{
await Task.Run(() => Console.WriteLine("Test"));
}
. . .
Foo().ConfigureAwait(false);
ConfigureAwait biedt een manier om het standaardgedrag van SynchronizationContext vastleggen te voorkomen; doorgeven van false voor de parameter flowContext voorkomt dat SynchronizationContext wordt gebruikt om de uitvoering na het wachten te hervatten.
Citaat uit It's All About the SynchronizationContext .
Waarom SynchronizationContext zo belangrijk is?
Beschouw dit voorbeeld:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = RunTooLong();
}
Met deze methode wordt de UI-toepassing bevroren totdat RunTooLong
is voltooid. De toepassing reageert niet.
U kunt proberen de binnencode asynchroon uit te voeren:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => label1.Text = RunTooLong());
}
Maar deze code wordt niet uitgevoerd omdat het interne deel kan worden uitgevoerd op een niet-UI-thread en het zou de UI-eigenschappen niet rechtstreeks moeten wijzigen :
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;
});
}
Vergeet nu niet altijd dit patroon te gebruiken. Of probeer SynchronizationContext.Post
die het voor u zal maken:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
SynchronizationContext.Current.Post((obj) =>
{
label1.Text = label1 Text);
}, null);
});
}