Las respuestas de SLaks y Killercam son buenas; Pensé que solo agregaría un poco más de contexto.
Su primera pregunta es esencialmente sobre qué métodos se pueden marcar async
.
Un método marcado como async
puede volver void
, Task
o Task<T>
. Cuáles son las diferencias entre ellos?
Se Task<T>
puede esperar un método asíncrono de retorno, y cuando la tarea se complete, ofrecerá una T.
Se Task
puede esperar un método asíncrono de retorno, y cuando la tarea se completa, la continuación de la tarea está programada para ejecutarse.
UNA void
método asíncrono de volver no puede ser esperada; Es un método de "dispara y olvida". Funciona de forma asíncrona, y no tienes forma de saber cuándo se hace. Esto es más que un poco extraño; como dice SLaks, normalmente solo lo haría al hacer un controlador de eventos asíncrono. El evento se dispara, el controlador se ejecuta; nadie va a "esperar" la tarea devuelta por el controlador de eventos porque los controladores de eventos no devuelven tareas, e incluso si lo hicieran, ¿qué código usaría la tarea para algo? En general, no es el código de usuario el que transfiere el control al controlador en primer lugar.
Su segunda pregunta, en un comentario, es esencialmente sobre lo que se puede await
editar:
¿Qué tipo de métodos se pueden await
editar? ¿Se puede editar un método de retorno de vacío await
?
No, no se puede esperar un método de retorno nulo. El compilador se traduce await M()
en una llamada a M().GetAwaiter()
, donde GetAwaiter
podría ser un método de instancia o un método de extensión. El valor esperado debe ser uno para el que pueda obtener un camarero; claramente, un método de retorno de vacío no produce un valor del cual se pueda obtener un camarero.
Task
-los métodos de retorno pueden producir valores esperados. Anticipamos que terceros querrán crear sus propias implementaciones de Task
objetos similares que se pueden esperar, y usted podrá esperarlos. Sin embargo, no se le permitirá declarar async
métodos que devuelvan cualquier cosa que no sea void
, Task
o Task<T>
.
(ACTUALIZACIÓN: Mi última oración allí puede ser falsificada por una versión futura de C #; hay una propuesta para permitir tipos de retorno que no sean tipos de tareas para métodos asíncronos).
(ACTUALIZACIÓN: La función mencionada anteriormente llegó a C # 7.)
async void
Los métodos plantean su excepción sobre el estadoSynchronizationContext
activo en el momento en que comenzaron a ejecutarse. Esto es similar al comportamiento de los controladores de eventos (sincrónicos). @DrewMarsh: laUnobservedTaskException
configuración y el tiempo de ejecución solo se aplican a los métodos de tareas asíncronas "disparar y olvidar" , no a losasync void
métodos.En caso de que la persona que llama quiera esperar la tarea o agregar una continuación.
De hecho, la única razón para regresar
void
es si no puede regresarTask
porque está escribiendo un controlador de eventos.fuente
void
, no tiene forma de llegar a la tarea que genera. (En realidad, no estoy seguro de si incluso genera unTask
absoluto)Los métodos regresan
Task
yTask<T>
son componibles, lo que significa que puedeawait
usarlos dentro de unasync
método.async
Los métodos de retornovoid
no son componibles, pero tienen otras dos propiedades importantes:El segundo punto es importante cuando se trata de un contexto que mantiene un recuento de operaciones asincrónicas pendientes.
El contexto ASP.NET es uno de esos contextos; Si utiliza
Task
métodos asincrónicos sin esperarlos de unvoid
método asincrónico , la solicitud de ASP.NET se completará demasiado pronto.Otro contexto es el
AsyncContext
que escribí para pruebas unitarias (disponible aquí ): elAsyncContext.Run
método rastrea el recuento de operaciones pendientes y regresa cuando es cero.fuente
Tipo
Task<T>
es el tipo de caballo de batalla de la Biblioteca Paralela de Tareas (TPL), representa el concepto de "algún trabajo / trabajo que va a producir un resultado de tipoT
en el futuro". El concepto de "trabajo que se completará en el futuro pero no devuelve ningún resultado" está representado por el tipo de tarea no genérico.Precisamente cómo se producirá el resultado del tipo
T
y los detalles de implementación de una tarea en particular; el trabajo puede asignarse a otro proceso en la máquina local, a otro subproceso, etc. Las tareas TPL generalmente se asignan a subprocesos de trabajo desde un grupo de subprocesos en el proceso actual, pero ese detalle de implementación no es fundamental para elTask<T>
tipo; más bien unTask<T>
puede representar cualquier operación de alta latencia que produce unT
.Según su comentario anterior:
La
await
expresión significa "evaluar esta expresión para obtener un objeto que represente el trabajo que producirá un resultado en el futuro. Registre el resto del método actual como la devolución de llamada asociada con la continuación de esa tarea. Una vez que se produce esa tarea y se devuelve la llamada está registrado, devuelva inmediatamente el control a mi interlocutor ". Esto es opuesto / en contraste con una llamada de método regular, lo que significa "recuerda lo que estás haciendo, ejecuta este método hasta que esté completamente terminado y luego retoma donde lo dejaste, ahora conociendo el resultado del método".Editar: Debería citar el artículo de Eric Lippert en la revista MSDN de octubre de 2011, ya que esto fue de gran ayuda para comprender esto en primer lugar.
Para más información y páginas blancas, vea aquí .
Espero que esto sea de ayuda.
fuente