C# Language
Contexte de synchronisation dans Async-Await
Recherche…
Pseudocode pour asynchrone / en attente de mots-clés
Considérons une méthode asynchrone simple:
async Task Foo()
{
Bar();
await Baz();
Qux();
}
En simplifiant, on peut dire que ce code signifie en réalité ce qui suit:
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;
}
Cela signifie que les mots-clés async
/ d' await
utilisent le contexte de synchronisation actuel s'il existe. C'est-à-dire que vous pouvez écrire du code de bibliothèque qui fonctionnerait correctement dans les applications d'interface utilisateur, Web et console.
Désactivation du contexte de synchronisation
Pour désactiver le contexte de synchronisation, vous devez appeler la méthode ConfigureAwait
:
async Task() Foo()
{
await Task.Run(() => Console.WriteLine("Test"));
}
. . .
Foo().ConfigureAwait(false);
ConfigureAwait fournit un moyen d'éviter le comportement de capture par défaut de SynchronizationContext; Le fait de transmettre false pour le paramètre flowContext empêche le SynchronizationContext d'être utilisé pour reprendre l'exécution après l'attente.
Citation de It's All About le SynchronizationContext .
Pourquoi SynchronizationContext est-il si important?
Considérez cet exemple:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = RunTooLong();
}
Cette méthode gèle l'application de l'interface utilisateur jusqu'à ce que RunTooLong
soit terminé. L'application ne répondra plus.
Vous pouvez essayer d'exécuter le code interne de manière asynchrone:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => label1.Text = RunTooLong());
}
Mais ce code ne s'exécutera pas car le corps interne peut être exécuté sur un thread non-interface utilisateur et il ne devrait pas modifier directement les propriétés de l'interface utilisateur :
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;
});
}
Maintenant, n'oubliez pas de toujours utiliser ce modèle. Ou, essayez SynchronizationContext.Post
qui le fera pour vous:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
SynchronizationContext.Current.Post((obj) =>
{
label1.Text = label1 Text);
}, null);
});
}