with run from create await async c# asynchronous .net-4.5

c# - run - async/await: ¿Cuándo devolver una Tarea vs nulo?



static async task c# return (6)

¿Bajo qué escenarios querría uno usar?

public async Task AsyncMethod(int num)

en lugar de

public async void AsyncMethod(int num)

El único escenario en el que puedo pensar es si necesita la tarea para poder realizar un seguimiento de su progreso.

Además, en el siguiente método, ¿son innecesarias las palabras asíncronas y de espera?

public static async void AsyncMethod2(int num) { await Task.Factory.StartNew(() => Thread.Sleep(num)); }


1) Normalmente, desearía devolver una Task . La excepción principal debe ser cuando necesita tener un tipo de retorno void (para eventos). Si no hay razón para no permitir que la persona await llama await su tarea, ¿por qué no permitirla?

2) los métodos async que devuelven el void son especiales en otro aspecto: representan operaciones asíncronas de nivel superior y tienen reglas adicionales que entran en juego cuando su tarea devuelve una excepción. La forma más fácil es mostrar la diferencia con un ejemplo:

static async void f() { await h(); } static async Task g() { await h(); } static async Task h() { throw new NotImplementedException(); } private void button1_Click(object sender, EventArgs e) { f(); } private void button2_Click(object sender, EventArgs e) { g(); } private void button3_Click(object sender, EventArgs e) { GC.Collect(); }

La excepción de f es siempre "observada". Una excepción que deja un método asíncrono de nivel superior se trata simplemente como cualquier otra excepción no controlada. Nunca se observa la excepción de g . Cuando el recolector de basura viene a limpiar la tarea, ve que la tarea resultó en una excepción, y nadie manejó la excepción. Cuando eso sucede, se TaskScheduler.UnobservedTaskException controlador TaskScheduler.UnobservedTaskException . Nunca debes dejar que esto suceda. Para usar tu ejemplo,

public static async void AsyncMethod2(int num) { await Task.Factory.StartNew(() => Thread.Sleep(num)); }

Sí, use async y await aquí, se aseguran de que su método aún funcione correctamente si se lanza una excepción.

Para obtener más información, consulte: http://msdn.microsoft.com/en-us/magazine/jj991977.aspx


Creo que también puede usar el async void para iniciar operaciones en segundo plano, siempre y cuando tenga cuidado de detectar las excepciones. ¿Pensamientos?

class Program { static bool isFinished = false; static void Main(string[] args) { // Kick off the background operation and don''t care about when it completes BackgroundWork(); Console.WriteLine("Press enter when you''re ready to stop the background operation."); Console.ReadLine(); isFinished = true; } // Using async void to kickoff a background operation that nobody wants to be notified about when it completes. static async void BackgroundWork() { // It''s important to catch exceptions so we don''t crash the appliation. try { // This operation will end after ten interations or when the app closes. Whichever happens first. for (var count = 1; count <= 10 && !isFinished; count++) { await Task.Delay(1000); Console.WriteLine($"{count} seconds of work elapsed."); } Console.WriteLine("Background operation came to an end."); } catch (Exception x) { Console.WriteLine("Caught exception:"); Console.WriteLine(x.ToString()); } } }


El problema de llamar async void es que ni siquiera recupera la tarea, no tiene forma de saber cuándo se ha completado la tarea de la función (consulte https://blogs.msdn.microsoft.com/oldnewthing/20170720-00/?p=96655 )

Aquí están las tres formas de llamar a una función asíncrona:

async Task<T> SomethingAsync() { ... return t; } async Task SomethingAsync() { ... } async void SomethingAsync() { ... }

En todos los casos, la función se transforma en una cadena de tareas. La diferencia es lo que devuelve la función.

En el primer caso, la función devuelve una tarea que eventualmente produce la t.

En el segundo caso, la función devuelve una tarea que no tiene producto, pero aún puede esperar en ella para saber cuándo se ha ejecutado hasta su finalización.

El tercer caso es el desagradable. El tercer caso es como el segundo, excepto que ni siquiera recuperas la tarea. No tiene forma de saber cuándo se ha completado la tarea de la función.

El caso de async void es un "disparar y olvidar": inicia la cadena de tareas, pero no le importa cuando haya terminado. Cuando la función regresa, todo lo que sabes es que todo hasta la primera espera se ha ejecutado. Todo después de la primera espera se ejecutará en algún punto no especificado en el futuro al que no tenga acceso.


Me he encontrado con este artículo muy útil sobre async y void escrito por Jérôme Laban: http://www.jaylee.org/post/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.aspx

La conclusión es que async+void puede bloquear el sistema y, por lo general, debe usarse solo en los controladores de eventos del lado de la interfaz de usuario.

La razón detrás de esto es el Contexto de Sincronización usado por AsyncVoidMethodBuilder, siendo ninguno en este ejemplo. Cuando no hay un Contexto de Sincronización ambiental, cualquier excepción que no sea manejada por el cuerpo de un método de vacío asíncrono se vuelve a generar en ThreadPool. Si bien parece que no hay otro lugar lógico donde se pueda lanzar ese tipo de excepción no controlada, el desafortunado efecto es que el proceso se termina, porque las excepciones no controladas en ThreadPool terminan efectivamente el proceso desde .NET 2.0. Puede interceptar todas las excepciones no controladas utilizando el evento AppDomain.UnhandledException, pero no hay forma de recuperar el proceso de este evento.

Al escribir controladores de eventos de IU, los métodos async void son de alguna manera indoloros porque las excepciones se tratan de la misma manera que en los métodos no asíncronos; Ellos son arrojados sobre el Despachador. Existe la posibilidad de recuperarse de dichas excepciones, lo cual es más que correcto para la mayoría de los casos. Sin embargo, fuera de los controladores de eventos de la interfaz de usuario, los métodos async void son de alguna manera peligrosos y pueden no ser tan fáciles de encontrar.


Mi respuesta es simple: usted, el cañón, espera el método del vacío. Error CS4008 No se puede esperar ''void'' TestAsync e: / test / TestAsync / TestAsync / Program.cs

Entonces, si el método es asíncrono, es mejor esperar, porque puede perder la ventaja asíncrona.


Tengo una idea clara de estas declaraciones.

  1. Los métodos de vacío asíncronos tienen diferentes semánticas de manejo de errores. Cuando se expulsa una excepción de un método de tarea asíncrona o de tarea asíncrona, esa excepción se captura y se coloca en el objeto de tarea. Con los métodos de vacío asíncrono, no hay ningún objeto de Tarea, por lo que cualquier excepción eliminada de un método de vacío asíncrono se generará directamente en el SynchronizationContext (SynchronizationContext representa una ubicación donde podría ejecutarse el código.) Que estaba activo cuando el método de vacío asíncrono empezado

Las excepciones de un método de Async Void no se pueden capturar con la captura

private async void ThrowExceptionAsync() { throw new InvalidOperationException(); } public void AsyncVoidExceptions_CannotBeCaughtByCatch() { try { ThrowExceptionAsync(); } catch (Exception) { // The exception is never caught here! throw; } }

Estas excepciones se pueden observar usando AppDomain.UnhandledException o un evento catch-all similar para aplicaciones GUI / ASP.NET, pero usar esos eventos para el manejo regular de excepciones es una receta para la imposibilidad de mantenimiento (bloquea la aplicación).

  1. Los métodos de vacío asíncronos tienen diferentes semánticas de composición. Los métodos asíncronos que devuelven la tarea o la tarea se pueden componer fácilmente utilizando await, Task.WhenAny, Task.WhenAll y así sucesivamente. Los métodos asíncronos que devuelven el vacío no proporcionan una forma fácil de notificar el código de llamada que han completado. Es fácil iniciar varios métodos de anulación asíncrona, pero no es fácil determinar cuándo han terminado. Los métodos de Async void notificarán a su SynchronizationContext cuando comiencen y terminen, pero un SynchronizationContext personalizado es una solución compleja para el código de aplicación regular.

  2. El método Async Void es útil cuando se usa un controlador de eventos síncrono porque elevan sus excepciones directamente en SynchronizationContext, que es similar a cómo se comportan los controladores de eventos síncronos

Para obtener más detalles, consulte este enlace https://msdn.microsoft.com/en-us/magazine/jj991977.aspx