C# Language
Contexto de sincronización en Async-Await
Buscar..
Pseudocódigo para palabras clave async / await
Considere un método asíncrono simple:
async Task Foo()
{
Bar();
await Baz();
Qux();
}
Simplificando, podemos decir que este código en realidad significa lo siguiente:
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;
}
Significa que las palabras clave async
/ await
usan el contexto de sincronización actual si existe. Es decir, puede escribir un código de biblioteca que funcione correctamente en las aplicaciones de UI, Web y Consola.
Deshabilitando el contexto de sincronización
Para deshabilitar el contexto de sincronización, debe llamar al método ConfigureAwait
:
async Task() Foo()
{
await Task.Run(() => Console.WriteLine("Test"));
}
. . .
Foo().ConfigureAwait(false);
ConfigureAwait proporciona un medio para evitar el comportamiento de captura predeterminado de SynchronizationContext; pasar falso para el parámetro flowContext evita que se use SynchronizationContext para reanudar la ejecución después de la espera.
Cita de Todo se trata del SynchronizationContext .
¿Por qué SynchronizationContext es tan importante?
Considera este ejemplo:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = RunTooLong();
}
Este método congelará la aplicación de la interfaz de usuario hasta que se complete el RunTooLong
. La aplicación no responderá.
Puede intentar ejecutar código interno de forma asíncrona:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => label1.Text = RunTooLong());
}
Pero este código no se ejecutará porque el cuerpo interno puede ejecutarse en un subproceso que no pertenece a la IU y no debería cambiar las propiedades de la IU directamente :
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;
});
}
Ahora no olvides usar siempre este patrón. O intente SynchronizationContext.Post
que lo hará por usted:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
var label1Text = RunTooLong();
SynchronizationContext.Current.Post((obj) =>
{
label1.Text = label1 Text);
}, null);
});
}