practices - c# private async void
Cómo llamar de forma segura a un método asíncrono en C#sin esperar (5)
La respuesta de Peter Ritchie fue lo que quería, y el artículo de Stephen Cleary sobre el regreso temprano en ASP.NET fue muy útil.
Sin embargo, como un problema más general (no específico para un contexto ASP.NET), la siguiente aplicación de la Consola demuestra el uso y el comportamiento de la respuesta de Peter utilizando 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()
devuelve temprano sin esperar a MyAsyncMethod()
y las excepciones lanzadas en MyAsyncMethod()
se tratan en OnMyAsyncMethodFailed(Task task)
y no en el try
/ catch
alrededor de GetStringData()
Tengo un método async
que no devuelve datos:
public async Task MyAsyncMethod()
{
// do some stuff async, don''t return any data
}
Llamo a esto desde otro método que devuelve algunos datos:
public string GetStringData()
{
MyAsyncMethod(); // this generates a warning and swallows exceptions
return "hello world";
}
Si llama a MyAsyncMethod()
sin esperar, MyAsyncMethod()
" Debido a que no se espera esta llamada, el método actual continúa ejecutándose antes de que se complete la llamada " en Visual Studio. En la página de esa advertencia dice:
Debería considerar suprimir la advertencia solo si está seguro de que no desea esperar a que finalice la llamada asíncrona y que el método llamado no genere 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 convocatoria podría plantear 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 un código que responde a una solicitud web en nuestro servicio web (ASP.NET Web API). La espera en un contexto de UI mantiene el subproceso de la interfaz de usuario libre, pero la espera en una solicitud de solicitud web esperará a que la tarea finalice antes de responder a la solicitud, lo que aumentará los tiempos de respuesta sin ninguna razón.
Primero debe considerar hacer de GetStringData
un método async
y hacer que await
la tarea devuelta por MyAsyncMethod
.
Si está absolutamente seguro de que no necesita manejar excepciones de MyAsyncMethod
o sabe cuándo se completa, puede hacer esto:
public string GetStringData()
{
var _ = MyAsyncMethod();
return "hello world";
}
Por cierto, esto 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 correctamente.
Actualizar:
Ya que está en ASP.NET y desea regresar temprano, puede encontrar útil la publicación de mi 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 dejar que se ejecute, pero no puede garantizarlo.
Entonces, esta es una buena solución para algo simple como lanzar un evento en un registro en el que realmente no importa si pierde algunos aquí y allá. No es una buena solución para cualquier tipo de operaciones críticas para el negocio. En esas situaciones, debe adoptar una arquitectura más compleja, con una forma persistente de guardar las operaciones (por ejemplo, Azure Queues, MSMQ) y un proceso en segundo plano separado (por ejemplo, Azure Worker Role, Win32 Service) para procesarlas.
Si desea obtener la excepción "asincrónicamente", puede hacer:
MyAsyncMethod().
ContinueWith(t => Console.WriteLine(t.Exception),
TaskContinuationOptions.OnlyOnFaulted);
Esto le permitirá lidiar con una excepción en un subproceso que no sea el subproceso "principal". Esto significa que no tiene que "esperar" la llamada a MyAsyncMethod()
desde el hilo que llama a MyAsyncMethod
; pero, todavía le permite hacer algo con una excepción, pero solo si se produce una excepción.
Actualizar:
Técnicamente, podrías hacer algo similar con await
:
try
{
await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
Trace.WriteLine(ex);
}
... lo cual sería útil si necesitara usar específicamente try
/ catch
(o using
) pero encuentro que ContinueWith
es un poco más explícito porque debe saber qué significa ConfigureAwait(false)
.
Supongo que surge la pregunta, ¿por qué necesitarías hacer esto? El motivo de async
en C # 5.0 es para que pueda esperar un resultado. Este método no es en realidad asíncrono, sino que se llama a la vez para no interferir demasiado con el hilo actual.
Quizás sea mejor comenzar un hilo y dejarlo para que termine solo.
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<ILog>();
log.Error("Unexpected Error", t.Exception);
}, TaskContinuationOptions.OnlyOnFaulted);
}