c# .net async-ctp async-await

¿Qué sucede con un subproceso en ''espera'' en C#Async CTP?



.net async-ctp (2)

He estado leyendo sobre la nueva palabra clave async await y suena increíble, pero hay una pregunta clave que no he podido encontrar la respuesta en ninguno de los videos de introducción que he visto hasta ahora (también leí el Libro blanco hace un tiempo atrás).

Supongamos que tengo una llamada para await en una función anidada en el subproceso de la interfaz de usuario principal. ¿Qué pasa con el hilo en este punto? ¿El control vuelve al bucle de mensajes y el subproceso de la interfaz de usuario es libre de procesar otras entradas?

Cuando se completa la tarea esperada, ¿se empuja la pila completa a una cola de mensajes, de manera que el control retornará a través de cada una de esas funciones anidadas, o algo más está sucediendo aquí?

Y en segundo lugar (aunque tengo su atención), realmente no entiendo por qué los métodos asíncronos necesitan ser etiquetados con async . ¿No se puede ejecutar ningún método de forma asíncrona? ¿Qué sucede si deseo ejecutar un método de forma asíncrona pero no tiene una palabra clave asíncrona? ¿Hay alguna forma de hacerlo de manera simple?

Aclamaciones. :)

Edit: Es cierto que si pudiera obtener el código de muestra de compilación, probablemente podría darme cuenta de eso, pero por una razón u otra me estoy topando con un bloque allí. Lo que realmente quiero saber es hasta qué punto continúa una continuación ... ¿congela toda la pila de llamadas, la reanuda cuando finaliza la tarea o solo regresa hasta ahora? ¿Es necesario que una función sea marcada como asíncrona para admitir la continuación, o (como pregunté originalmente) continúa la pila de llamadas completa?

Si no congela toda la pila de llamadas, ¿qué sucede cuando la espera asíncrona llega a una función de llamada no asíncrona? ¿Se bloquea allí? ¿No sería eso derrotar el punto de espera? Espero que puedan ver que me falta algo de comprensión aquí, que espero que alguien pueda completar para poder continuar aprendiendo esto.


Supongamos que tengo una llamada para esperar en una función anidada en el subproceso de la interfaz de usuario principal. ¿Qué pasa con el hilo en este punto? ¿El control vuelve al bucle de mensajes y el subproceso de la interfaz de usuario es libre de procesar otras entradas?

Sí. Cuando await una respuesta esperada (como una Task<TResult> ), se captura la posición actual del hilo dentro del método async . A continuación, pone en cola el resto del método (la "continuación") que se ejecutará cuando finalice lo esperado (p. Ej., Cuando se Task<TResult> una Task<TResult> ).

Sin embargo, hay una optimización que puede tener lugar: si lo que está esperando ya está terminado, entonces await no tiene que esperar, y simplemente continúa ejecutando el método inmediatamente. Esto se llama el "camino rápido", descrito aquí .

Cuando se completa la tarea esperada, ¿se empuja la pila completa a una cola de mensajes, de manera que el control retornará a través de cada una de esas funciones anidadas, o algo más está sucediendo aquí?

La posición actual del hilo se inserta en la cola de mensajes de la interfaz de usuario. Los detalles son un poco más complejos: las continuaciones se programan en TaskScheduler.FromCurrentSynchronizationContext menos que SynchronizationContext.Current sea null , en cuyo caso se programan en TaskScheduler.Current . Además, este comportamiento se puede anular llamando a ConfigureAwait(false) , que siempre programa la continuación en el grupo de subprocesos. Dado que SynchronizationContext.Current es un UC SynchronizationContext para WPF / WinForms / Silverlight, esta continuación se inserta en la cola de mensajes de la IU.

Y en segundo lugar (aunque tengo su atención), realmente no entiendo por qué los métodos asíncronos necesitan ser etiquetados con asíncrono. ¿No se puede ejecutar ningún método de forma asíncrona? ¿Qué sucede si deseo ejecutar un método de forma asíncrona pero no tiene una palabra clave asíncrona? ¿Hay alguna forma de hacerlo de manera simple?

Estos son significados ligeramente diferentes de la palabra "asíncrono". La palabra clave async habilita la palabra clave await . En otras palabras, los métodos async pueden await . Los delegados asíncronos pasados ​​de moda (es decir, BeginInvoke / EndInvoke ) son bastante diferentes de async . Los delegados asíncronos se ejecutan en un subproceso de ThreadPool , pero los métodos async ejecutan en el subproceso de la interfaz de usuario (suponiendo que se llaman desde un contexto de la interfaz de usuario y no se llama a ConfigureAwait(false) ).

Si desea que se ejecute un método (no async ) en un subproceso de ThreadPool , puede hacerlo de la siguiente manera:

await Task.Run(() => MyMethod(..));

Lo que realmente quiero saber es hasta qué punto continúa una continuación ... ¿congela toda la pila de llamadas, la reanuda cuando finaliza la tarea o solo regresa hasta ahora? ¿Es necesario que una función sea marcada como asíncrona para admitir la continuación, o (como pregunté originalmente) continúa la pila de llamadas completa?

La posición actual se captura y se "reanuda" cuando se ejecuta la continuación. Cualquier función que utilice la await para admitir las continuaciones debe marcarse como async .

Si está llamando a un método async desde un método no async , entonces debe tratar el objeto Task directamente. Esto normalmente no se hace. Los métodos async nivel superior pueden devolver el void , por lo que no hay razón para no tener controladores de eventos async .

Tenga en cuenta que async es puramente una transformación de compilador. Eso significa que los métodos async son exactamente como los métodos normales después de compilarlos. El tiempo de ejecución .NET no los trata de ninguna manera especial.


Depende del comportamiento de Awaitable.

Tiene la opción de ejecutarse sincrónicamente, es decir, se ejecuta en el subproceso y devuelve el control al observador en el mismo subproceso.

Si elige ejecutarse de forma asíncrona, se llamará al esperante en el hilo en el que el programable espera la devolución de llamada. Mientras tanto, se libera el subproceso de llamada, ya que hay un inicio que se puede esperar, su trabajo es asíncrono y se ha salido, y se ha adjuntado al espectador su continuación a la devolución de llamada del esperable.

En cuanto a su segunda pregunta, la palabra clave asíncrona no se refiere a si el método se llama de forma asíncrona o no, sino a si el cuerpo de ese método desea llamar el código asíncrono en línea.

Es decir, cualquier método que devuelva Tarea o Tarea puede llamarse de forma asíncrona (en espera o con continuar con), pero al marcarlo con asíncrono, ese método ahora puede usar la palabra clave await en su cuerpo y cuando devuelve no devuelve una Tarea, pero simplemente T, ya que todo el cuerpo se reescribirá en una máquina de estado que ejecuta la tarea.

Digamos que tienes metodo

public async Task DoSomething() { }

cuando lo llamas desde el hilo de la interfaz de usuario, recuperas una tarea. En este punto, puede bloquear en la tarea con .Wait() o .Result que ejecuta el método asíncrono en su TaskScheduler o bloque con .RunSynchronously() que lo ejecutará en el subproceso de la interfaz de usuario. Por supuesto, cualquier espera que ocurra dentro de DoSomething es básicamente otra continuación, por lo que podría terminar ejecutando parte del código en un subproceso TaskScheduler. Pero al final, el subproceso de la interfaz de usuario se bloquea hasta su finalización, al igual que un método síncrono regular.

O puede programar una continuación con .ContinueWith() que creará una acción a la que TaskScheduler llamará cuando finalice la Tarea. Esto devolverá inmediatamente el control al código actual que continúa haciendo lo que estaba haciendo en el subproceso de la interfaz de usuario. La continuación no captura la pila de llamadas, es simplemente una acción, por lo que simplemente captura las variables a las que accede desde su ámbito externo.