Estoy tratando de entender por qué un async método void en un ASP.Net la aplicación puede resultar en la siguiente excepción, aunque parece que async Tarea no:

System.InvalidOperationException: An asynchronous module or handler 
completed while an asynchronous operation was still pending

Yo soy relativamente nuevo en el mundo de async en .NET, pero no se siente como he tratado de ejecutar esta abajo a través de un número de recursos existentes, incluyendo todos de los siguientes:

De estos recursos, entiendo que la mejor práctica es típicamente retorno de la Tarea y evitar async vacío. También entiendo que async void incrementa el contador de operaciones pendientes cuando se llama al método y se decrementa cuando se haya completado. Esto suena como que al menos parte de la respuesta a mi pregunta. Sin embargo, lo que me falta es lo que sucede cuando regrese de la Tarea y por qué hacerlo hace que las cosas «trabajo».

Aquí es un ejemplo inventado para ilustrar mi pregunta:

public class HomeController : AsyncController
{
    //This method will work fine
    public async Task<ActionResult> ThisPageWillLoad()
    {
        //Do not await the task since it is meant to be fire and forget
        var task = this.FireAndForgetTask();

        return await Task.FromResult(this.View("Index"));
    }

    private async Task FireAndForgetTask()
    {
        var task = Task.Delay(TimeSpan.FromSeconds(3));
        await task;
    }

    //This method will throw the following exception:
    //System.InvalidOperationException: An asynchronous module or 
    //handler completed while an asynchronous operation was still pending
    public async Task<ActionResult> ThisPageWillNotLoad()
    {
        //Obviously can't await a void method
        this.FireAndForgetVoid();

        return await Task.FromResult(this.View("Index"));
    }

    private async void FireAndForgetVoid()
    {
        var task = Task.Delay(TimeSpan.FromSeconds(3));
        await task;
    }
}

En una nota relacionada, si mi comprensión de async nula es correcta, entonces ¿no es una especie de mal pensar de async vacío como «disparar y olvidar» en este escenario ya que ASP.Net en realidad, no es olvidarlo?

  • async void es «disparar y olvidar» en la medida en que no puede await, porque no devuelve un disponible Task.
InformationsquelleAutor David Kreps | 2013-07-15

1 Comentario

  1. 59

    Microsoft tomó la decisión para evitar tanto de compatibilidad hacia atrás de problemas como sea posible cuando se lleva async en ASP.NET. Y se lo quiso llevar a todos sus «uno ASP.NET» – así que async apoyo para WinForms, MVC, WebAPI, SignalR, etc.

    Históricamente, ASP.NET ha apoyado a limpiar las operaciones asincrónicas desde entonces .NET 2.0 a través del modelo Asincrónico basado Evento-(EAP), en el que asincrónica componentes de notificar a la SynchronizationContext de su inicio y finalización. .NET 4.5 trae la primera bastante fuertes cambios a este apoyo, la actualización del núcleo ASP.NET asincrónica tipos para facilitar la Tarea-modelo Asincrónico basado (TAP, es decir, async).

    Mientras tanto, cada uno de los diferentes framework (WebForms, MVC, etc) todas desarrollado su propia manera de interactuar con ese núcleo, manteniendo compatibilidad hacia atrás una prioridad. En un intento de ayudar a los desarrolladores, el núcleo ASP.NET SynchronizationContext se ha mejorado con la excepción de que estamos viendo; que cogerá el uso de muchos errores.

    En los Formularios mundo, han RegisterAsyncTask pero un montón de gente utiliza async void controladores de eventos en su lugar. Por lo que el ASP.NET SynchronizationContext permitirá async void en el momento adecuado durante el ciclo de vida de página, y si lo usa en un momento inadecuado provocará que la excepción.

    En el MVC/WebAPI/SignalR mundo, los marcos son más estructurados como el de los servicios. Por lo que fueron capaces de adoptar async Task de un modo muy natural de la moda, y el marco sólo tiene que lidiar con la devuelve Task – muy limpio abstracción. Como una nota del lado, usted no necesita AsyncController más; MVC sabe que es asincrónica sólo porque devuelve un Task.

    Sin embargo, si intenta devolver un Task y uso async void, que no se admite. Y hay pocas razones para apoyarla; sería bastante complejo sólo para apoyar a los usuarios que no se supone que para hacer eso de todos modos. Recuerde que async void notifica el núcleo ASP.NET SynchronizationContext directamente, sin pasar por el marco de MVC completo. El marco de MVC entiende cómo esperar para su Task pero aún no sabe acerca de la async void, por lo que se devuelve a la finalización ASP.NET que se vea que no en realidad completa.

    Esto puede causar problemas en dos escenarios:

    1. Estás tratando de usar alguna librería o lo que sea que utiliza async void. Lo siento, pero el hecho es que la biblioteca está roto, y tendrá que ser fijo.
    2. Estás envolviendo un EAP de componentes en un Task y utilizar adecuadamente los await. Esto puede causar problemas debido a la EAP componente interactúa con SynchronizationContext directamente. En este caso, la mejor solución es modificar el tipo de modo que es compatible TOQUE natural o reemplazarlo con un TOQUE tipo (por ejemplo, HttpClient en lugar de WebClient). En su defecto, puede utilizar TAP-sobre-APM en lugar de TAP-sobre-EAP. Si ninguna de estas son factibles, sólo se puede utilizar Task.Run alrededor de su TOQUE-sobre-EAP contenedor.

    Respecto a «disparar y olvidar»:

    Yo personalmente nunca uso esta frase para async void métodos. Por un lado, el manejo de errores de semántica ciertamente no encaja con la frase «dispara y olvida»; yo medio en broma, se refieren a async void métodos como «fuego y choque». Un verdadero async «disparar y olvidar» método sería una async Task método con el que se ignore la devuelve Task en lugar de esperar.

    Que dijo, en ASP.NET casi nunca quiero regresar antes de las solicitudes (que es lo que «dispara y olvida» implica). Esta respuesta ya es muy largo, pero tengo un descripción de los problemas en mi blog, junto con un poco de código para apoyar ASP.NET «disparar y olvidar» si es realmente necesario.

    • Incendio y accidente…agradable. Gracias por la respuesta detallada. La pieza que falta todavía para mí es lo que sucede cuando hago «var task = este.FireAndForgetTask()». ¿Qué hace el marco de realmente hacer con la tarea que se devuelve? No estoy de guardarlo en cualquier lugar ni estoy siempre a la espera. En este caso, es acercarla al fuego y olvidar? Ya no estamos en los EAP de la tierra, no hay ningún código que interactúa directamente con el SynchronizationContext, y no a la espera de la tarea verdaderamente se olvida de él?
    • Si usted no use nunca la task, entonces nada de lo que se usa. El marco no sabe que existe. Sí, eso es «disparar y olvidar» pero tenga en cuenta las advertencias en mi blog sobre cómo hacerlo.
    • Excelente. Creo que lo entiendo ahora. Gracias por la respuesta y los enlaces adicionales muy útiles.
    • Yo estoy en una situación similar. Puede usted por favor decirme lo que hizo para lograr lo anterior? Gracias
    • Creo que no entiendo tu pregunta. Que parte de lo que está escrito arriba está buscando una aclaración sobre?
    • Estoy recibiendo este error, pero no tengo async vacío en mi código

Dejar respuesta

Please enter your comment!
Please enter your name here