c# async - Supresión de "advertencia CS4014:como no se espera esta llamada, la ejecución del método actual continúa..."




await explained (7)

El motivo de la advertencia es que WorkAsync devuelve una Tarea que nunca se lee ni espera. Puede establecer que el tipo de devolución de WorkAsync se anule y la advertencia desaparecerá.

Normalmente, un método devuelve una Tarea cuando la persona que llama necesita conocer el estado del trabajador. En el caso de un "olvídate del fuego", el vacío debe devolverse para asemejarse a que la persona que llama es independiente del método llamado.

static async void WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}

static async Task StartWorkAsync()
{
    WorkAsync(); // no warning since return type is void

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

Esto no es un duplicado de "Cómo llamar sin peligro un método asíncrono en C # sin esperar" .

¿Cómo puedo suprimir bien la siguiente advertencia?

Advertencia CS4014: Debido a que no se espera esta llamada, la ejecución del método actual continúa antes de que se complete la llamada. Considere la posibilidad de aplicar el operador 'esperar' al resultado de la llamada.

Un simple ejemplo:

static async Task WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}

static async Task StartWorkAsync()
{
    WorkAsync(); // I want fire-and-forget 

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

Lo que probé y no me gustó:

static async Task StartWorkAsync()
{
    #pragma warning disable 4014
    WorkAsync(); // I want fire-and-forget here
    #pragma warning restore 4014
    // ...
}

static async Task StartWorkAsync()
{
    var ignoreMe = WorkAsync(); // I want fire-and-forget here
    // ...
}

Puede crear un método de extensión que evitará la advertencia. El método de extensión puede estar vacío o puede agregar el manejo de excepciones con .ContinueWith() allí.

static class TaskExtensions
{
    public static void Forget(this Task task)
    {
        task.ContinueWith(
            t => { WriteLog(t.Exception); },
            TaskContinuationOptions.OnlyOnFaulted);
    }
}

public async Task StartWorkAsync()
{
    this.WorkAsync().Forget();
}

Sin embargo, ASP.NET cuenta el número de tareas en ejecución, por lo que no funcionará con la extensión simple Forget() como se indicó anteriormente y, en su lugar, puede fallar con la excepción:

Un módulo o controlador asíncrono completado mientras que una operación asincrónica todavía estaba pendiente.

Con .NET 4.5.2 se puede resolver utilizando HostingEnvironment.QueueBackgroundWorkItem :

public static Task HandleFault(this Task task, CancellationToken cancelToken)
{
    return task.ContinueWith(
        t => { WriteLog(t.Exception); },
        cancelToken,
        TaskContinuationOptions.OnlyOnFaulted,
        TaskScheduler.Default);
}

public async Task StartWorkAsync()
{
    System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(
        cancelToken => this.WorkAsync().HandleFault(cancelToken));
}

Puede decorar el método con el siguiente atributo:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
{
    WorkAsync();
    // ...
}

Básicamente le está diciendo al compilador que sabe lo que está haciendo y que no necesita preocuparse por un posible error.

La parte importante de este código es el segundo parámetro. La parte "CS4014:" es lo que suprime la advertencia. Puedes escribir todo lo que quieras en el resto.

Saludos, Fábio


¿Por qué no envolverlo dentro de un método asíncrono que devuelve vacío? Un poco largo, pero se usan todas las variables.

static async Task StartWorkAsync()
{   
     async void WorkAndForgetAsync() => await WorkAsync();
     WorkAndForgetAsync(); // no warning
}

Una manera fácil de detener la advertencia es simplemente asignar la tarea al llamarla:

Task fireAndForget = WorkAsync(); // No warning now

Y así en tu publicación original harías:

static async Task StartWorkAsync()
{
    // Fire and forget
    var fireAndForget = WorkAsync(); // Tell the compiler you know it's a task that's being returned 

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

Con C # 7 ahora puedes usar discards :

_ = WorkAsync();

Tenga en cuenta que cuando habla de MSIL, está hablando de instrucciones para una máquina virtual . La máquina virtual utilizada en .NET es una máquina virtual basada en pila. A diferencia de una máquina virtual basada en el registro, la máquina virtual Dalvik utilizada en los sistemas operativos Android es un ejemplo de ello.

La pila en la máquina virtual es virtual, le corresponde al intérprete o al compilador justo a tiempo traducir las instrucciones de la máquina virtual en el código real que se ejecuta en el procesador. Lo que en el caso de .NET es casi siempre un jitter, el conjunto de instrucciones MSIL fue diseñado para ser jit desde el principio. A diferencia del código de bytes de Java, por ejemplo, tiene instrucciones distintas para las operaciones en tipos de datos específicos. Lo que lo hace optimizado para ser interpretado. Sin embargo, en realidad existe un intérprete de MSIL, se usa en .NET Micro Framework. Que se ejecuta en procesadores con recursos muy limitados, no puede pagar la RAM necesaria para almacenar el código de la máquina.

El modelo de código de máquina real es mixto, con una pila y registros. Uno de los grandes trabajos del optimizador de código JIT es encontrar formas de almacenar variables que se mantienen en la pila en los registros, lo que mejora considerablemente la velocidad de ejecución. Un jitter Dalvik tiene el problema opuesto.

De lo contrario, la pila de máquinas es una instalación de almacenamiento muy básica que ha estado presente en los diseños de procesadores durante mucho tiempo. Tiene una muy buena localidad de referencia, una característica muy importante en las CPU modernas que mastican los datos mucho más rápido de lo que la RAM puede proporcionar y admite la recursión. El diseño del lenguaje está muy influenciado por tener una pila, visible en el soporte para variables locales y alcance limitado al cuerpo del método. Un problema importante con la pila es el nombre de este sitio.





c# async-await