understanding - C#Async-¿Cómo funciona?
task whenall c# (3)
Microsoft anunció el Visual Studio Async CTP hoy (28 de octubre de 2010) que introduce la async
y await
palabras clave en C # / VB para la ejecución asíncrona del método.
Primero pensé que el compilador traduce las palabras clave en la creación de un hilo, pero de acuerdo con el libro blanco y la presentación PDC de Anders Hejlsberg (a las 31:00), la operación asincrónica ocurre completamente en el hilo principal.
¿Cómo puedo tener una operación ejecutada en paralelo en el mismo hilo? ¿Cómo es técnicamente posible y cuál es la característica realmente traducida en IL?
¿Cómo puedo tener una operación ejecutada en paralelo en el mismo hilo?
No puedes. La asincronía no es "paralelismo" o "concurrencia" . La asincronía podría implementarse con paralelismo, o podría no serlo. Podría implementarse dividiendo el trabajo en pequeños trozos, poniendo cada porción de trabajo en una cola y luego ejecutando cada porción de trabajo siempre que el hilo no esté haciendo otra cosa.
Tengo una serie completa de artículos en mi blog sobre cómo funciona todo esto; el directamente relacionado con esta pregunta probablemente subirá el jueves de la próxima semana. Reloj
http://blogs.msdn.com/b/ericlippert/archive/tags/async/
para detalles.
Funciona de manera similar a la palabra clave yield return
en C # 2.0.
Un método asíncrono no es en realidad un método secuencial ordinario. Se compila en una máquina de estado (un objeto) con algún estado (las variables locales se convierten en campos del objeto). Cada bloque de código entre dos usos de await
es un "paso" de la máquina de estado.
Esto significa que cuando se inicia el método, solo ejecuta el primer paso y luego la máquina de estados regresa y programa un poco de trabajo por realizar: cuando el trabajo finalice, se ejecutará el siguiente paso de la máquina de estados. Por ejemplo, este código:
async Task Demo() {
var v1 = foo();
var v2 = await bar();
more(v1, v2);
}
Sería traducido a algo así como:
class _Demo {
int _v1, _v2;
int _state = 0;
Task<int> _await1;
public void Step() {
switch(this._state) {
case 0:
this._v1 = foo();
this._await1 = bar();
// When the async operation completes, it will call this method
this._state = 1;
op.SetContinuation(Step);
case 1:
this._v2 = this._await1.Result; // Get the result of the operation
more(this._v1, this._v2);
}
}
La parte importante es que solo usa el método SetContinuation
para especificar que cuando la operación finalice, debería volver a llamar al método Step
(y el método sabe que debe ejecutar el segundo bit del código original usando el campo _state
). Puede imaginarse fácilmente que SetContinuation
sería algo así como btn.Click += Step
, que se ejecutaría completamente en un único hilo.
El modelo de programación asíncrono en C # está muy cerca de los flujos de trabajo asíncronos F # (de hecho, es esencialmente lo mismo, aparte de algunos detalles técnicos), y la escritura de aplicaciones GUI de un solo hilo reactivo usando async
es un área bastante interesante - al menos yo piense así, vea por ejemplo este artículo (tal vez debería escribir una versión de C # ahora :-)).
La traducción es similar a los iteradores (y yield return
) y, de hecho, fue posible utilizar iteradores para implementar programación asincrónica en C # antes. Escribí un artículo sobre eso hace un tiempo, y creo que todavía puede darle una idea de cómo funciona la traducción.
Según entiendo, lo que hacen las palabras clave async
y await
es que cada vez que un método async
emplea la palabra clave await
, el compilador convertirá el resto del método en una continuación que está programada cuando se completa la operación asincrónica. Esto permite que los métodos async
vuelvan a la persona que llama de inmediato y reanude el trabajo cuando se realiza la parte de sincronización.
De acuerdo con los documentos disponibles hay muchos detalles, pero a menos que esté equivocado, esa es la esencia de esto.
Como lo veo, el propósito de los métodos asíncronos no es ejecutar una gran cantidad de código en paralelo, sino cortar los métodos asíncronos en varios trozos pequeños, que se pueden llamar según sea necesario. El punto clave es que el compilador manejará todo el complejo cableado de devoluciones de llamadas mediante tareas / continuaciones. Esto no solo reduce la complejidad, sino que permite que el método asíncrono se escriba más o menos como el código síncrono tradicional.