Cómo llamar de forma segura un método asíncrono en C # sin esperar

Tengo un método async que no devuelve datos:

 public async Task MyAsyncMethod() { // do some stuff async, don't return any data } 

Estoy llamando a esto desde otro método que devuelve algunos datos:

 public string GetStringData() { MyAsyncMethod(); // this generates a warning and swallows exceptions return "hello world"; } 

Llamar a MyAsyncMethod() sin MyAsyncMethod() provoca un MyAsyncMethod() “En esta aplicación, el MyAsyncMethod() ” Dado que no se espera esta llamada, el método actual continúa ejecutándose antes de que se complete la llamada “. En la página para esa advertencia, dice:

Debe considerar suprimir la advertencia solo si está seguro de que no desea esperar a que se complete la llamada asincrónica y de que el método llamado no generará ninguna excepción .

Estoy seguro de que no quiero esperar a que se complete la llamada; No necesito ni tengo tiempo para hacerlo. Pero la llamada puede generar excepciones.

Me he topado con este problema varias veces y estoy seguro de que es un problema común que debe tener una solución común.

¿Cómo puedo llamar de forma segura a un método asíncrono sin esperar el resultado?

Actualizar:

Para las personas que sugieren que solo espero el resultado, este es el código que responde a una solicitud web en nuestro servicio web (ASP.NET Web API). Esperar en un contexto de interfaz de usuario mantiene el hilo de la interfaz de usuario libre, pero esperar en una solicitud web esperará a que la tarea finalice antes de responder a la solicitud, lo que aumenta los tiempos de respuesta sin ningún motivo.

Si desea obtener la excepción “asincrónicamente”, podría hacer lo siguiente:

  MyAsyncMethod(). ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted); 

Esto le permitirá tratar con una excepción en un hilo que no sea el hilo “principal”. Esto significa que no tiene que “esperar” la llamada a MyAsyncMethod() desde el hilo que llama a MyAsyncMethod ; pero, aún le permite hacer algo con una excepción, pero solo si ocurre una excepción.

Actualizar:

técnicamente, podrías hacer algo similar con la await :

 try { await MyAsyncMethod().ConfigureAwait(false); } catch (Exception ex) { Trace.WriteLine(ex); } 

… que sería útil si necesitaras usar try / catch (o using ) específicamente, pero creo que el ContinueWith es un poco más explícito porque debes saber lo que significa ConfigureAwait(false) .

En primer lugar, debería considerar hacer de GetStringData un método async y await la tarea devuelta desde MyAsyncMethod .

Si está absolutamente seguro de que no necesita manejar excepciones de MyAsyncMethod o saber cuándo se completa, puede hacerlo:

 public string GetStringData() { var _ = MyAsyncMethod(); return "hello world"; } 

Por cierto, este no es un “problema común”. Es muy raro querer ejecutar algún código y no importa si se completa y no importa si se completa con éxito.

Actualizar:

Ya que está en ASP.NET y quiere regresar temprano, puede encontrar útil mi publicación en el blog sobre el tema . Sin embargo, ASP.NET no fue diseñado para esto, y no hay garantía de que su código se ejecutará después de que se devuelva la respuesta. ASP.NET hará todo lo posible para que se ejecute, pero no puede garantizarlo.

Entonces, esta es una buena solución para algo tan simple como lanzar un evento en un registro donde realmente no importa si pierdes algunos aquí y allá. No es una buena solución para ningún tipo de operaciones críticas para la empresa. En esas situaciones, debe adoptar una architecture más compleja, con una forma persistente de guardar las operaciones (por ejemplo, Azure Queues, MSMQ) y un proceso de fondo separado (por ejemplo, Azure Worker Role, Win32 Service) para procesarlas.

La respuesta de Peter Ritchie fue lo que quería, y el artículo de Stephen Cleary sobre regresar temprano en ASP.NET fue muy útil.

Sin embargo, como un problema más general (no específico de un contexto ASP.NET), la siguiente aplicación de consola demuestra el uso y el comportamiento de la respuesta de Peter usando Task.ContinueWith(...)

 static void Main(string[] args) { try { // output "hello world" as method returns early Console.WriteLine(GetStringData()); } catch { // Exception is NOT caught here } Console.ReadLine(); } public static string GetStringData() { MyAsyncMethod().ContinueWith(OnMyAsyncMethodFailed, TaskContinuationOptions.OnlyOnFaulted); return "hello world"; } public static async Task MyAsyncMethod() { await Task.Run(() => { throw new Exception("thrown on background thread"); }); } public static void OnMyAsyncMethodFailed(Task task) { Exception ex = task.Exception; // Deal with exceptions here however you want } 

GetStringData() regresa temprano sin esperar MyAsyncMethod() y las excepciones lanzadas en MyAsyncMethod() se tratan en OnMyAsyncMethodFailed(Task task) y no en try / catch alrededor de GetStringData()

Termino con esta solución:

 public async Task MyAsyncMethod() { // do some stuff async, don't return any data } public string GetStringData() { // Run async, no warning, exception are catched RunAsync(MyAsyncMethod()); return "hello world"; } private void RunAsync(Task task) { task.ContinueWith(t => { ILog log = ServiceLocator.Current.GetInstance(); log.Error("Unexpected Error", t.Exception); }, TaskContinuationOptions.OnlyOnFaulted); } 

Supongo que surge la pregunta, ¿por qué tendrías que hacer esto? El motivo de la async en C # 5.0 es que puede esperar un resultado. Este método no es realmente asíncrono, simplemente se llama a la vez para no interferir demasiado con el hilo actual.

Tal vez sea mejor comenzar un hilo y dejarlo terminar por sí mismo.