Creo que te estás confundiendo algunas cosas, aquí. Lo que está pidiendo ya es posible usar System.Threading.Tasks
, async
y await
en C # 5 solo proporcionará un poco de azúcar sintáctica más agradable para la misma característica.
Usemos un ejemplo de Winforms: suelte un botón y un cuadro de texto en el formulario y use este código:
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew<int>(() => DelayedAdd(5, 10))
.ContinueWith(t => DelayedAdd(t.Result, 20))
.ContinueWith(t => DelayedAdd(t.Result, 30))
.ContinueWith(t => DelayedAdd(t.Result, 50))
.ContinueWith(t => textBox1.Text = t.Result.ToString(),
TaskScheduler.FromCurrentSynchronizationContext());
}
private int DelayedAdd(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Ejecútelo y verá que (a) no bloquea el subproceso de la interfaz de usuario y (b) no obtiene el error habitual "la operación de subprocesos no es válida", a menos que elimine el TaskScheduler
argumento del último ContinueWith
, en en cuyo caso lo harás.
Este es un estilo de pase de continuación estándar de pantano . La magia ocurre en la TaskScheduler
clase y específicamente en la instancia recuperada por FromCurrentSynchronizationContext
. Pase esto a cualquier continuación y le dirá que la continuación debe ejecutarse en cualquier hilo llamado FromCurrentSynchronizationContext
método, en este caso, el hilo UI.
Los aguardadores son un poco más sofisticados en el sentido de que son conscientes de en qué hilo comenzaron y en qué hilo debe suceder la continuación. Entonces, el código anterior se puede escribir un poco más naturalmente:
private async void button1_Click(object sender, EventArgs e)
{
int a = await DelayedAddAsync(5, 10);
int b = await DelayedAddAsync(a, 20);
int c = await DelayedAddAsync(b, 30);
int d = await DelayedAddAsync(c, 50);
textBox1.Text = d.ToString();
}
private async Task<int> DelayedAddAsync(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Estos dos deberían ser muy similares, y de hecho son muy similares. El DelayedAddAsync
método ahora devuelve un en Task<int>
lugar de un int
, y por lo tanto await
solo está aplicando continuaciones a cada uno de ellos. La principal diferencia es que está pasando el contexto de sincronización en cada línea, por lo que no tiene que hacerlo explícitamente como lo hicimos en el último ejemplo.
En teoría, las diferencias son mucho más significativas. En el segundo ejemplo, cada línea del button1_Click
método se ejecuta en el subproceso de la interfaz de usuario, pero la tarea en sí ( DelayedAddAsync
) se ejecuta en segundo plano. En el primer ejemplo, todo se ejecuta en segundo plano , excepto la asignación a la textBox1.Text
que nos hemos asociado explícitamente al contexto de sincronización del hilo de la interfaz de usuario.
Eso es lo que es realmente interesante await
: el hecho de que un camarero puede entrar y salir del mismo método sin bloquear ninguna llamada. Usted llama await
, el hilo actual vuelve a procesar los mensajes, y cuando está hecho, el camarero retomará exactamente donde lo dejó, en el mismo hilo que dejó. Pero en términos de su Invoke
/ BeginInvoke
contraste en la pregunta, yo ' Lamento decir que deberías haber dejado de hacerlo hace mucho tiempo.
await
funcionalidad. Es solo una gran cantidad de azúcar sintáctica para el paso continuo . ¿Posiblemente hay otras mejoras no relacionadas con WinForms que se supone que ayudan? Sin embargo, eso caería dentro del marco .NET en sí, y no en C # específicamente.