Supresión de “advertencia CS4014: como no se espera esta llamada, la ejecución del método actual continúa …”

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, eg: // ... 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 restre 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)); } 

Con C # 7 ahora puedes usar descartes :

 _ = WorkAsync(); 

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 comstackdor 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 rest.

Mi dos formas de lidiar con esto.

Solo suprímalo

#pragma warning disable es una solución lo suficientemente agradable para “disparar y olvidar”.

El motivo por el que existe esta advertencia es porque en muchos casos no es su intención utilizar un método que devuelva la tarea sin esperarla. Suprimir la advertencia cuando tienes la intención de disparar y olvidar tiene sentido.

Si tiene problemas para recordar cómo deletrear #pragma warning disable 4014 , simplemente deje que Visual Studio lo agregue. Presiona Ctrl +. para abrir “Acciones rápidas” y luego “Suprimir CS2014”

Guárdelo en una variable local

Ejemplo

var task = Task.Run(() => DoMyStuff()).ConfigureAwait(false);

Como no hay una llamada a la task posterior, en el modo de lanzamiento, la variable local se debe optimizar de inmediato, como si nunca hubiera existido. De hecho, dudo que el comstackdor incluso cree la variable local (alguien puede confirmarlo con un decomstackdor).

Considerándolo todo

Es estúpido crear un método que requiera algunos tics más para ejecutarse, solo con el propósito de suprimir una advertencia.

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, eg: // ... await Task.Delay(2000); } 

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, eg: // ... await Task.Delay(2000); } 

¿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 } 

Encontré este enfoque por accidente hoy. Puede definir un delegado y asignar primero el método asincrónico al delegado.

  delegate Task IntermediateHandler(); static async Task AsyncOperation() { await Task.Yield(); } 

y llámalo así

 (new IntermediateHandler(AsyncOperation))(); 

Pensé que era interesante que el comstackdor no diera exactamente la misma advertencia al usar el delegado.