async / await - ¿cuándo devolver una tarea vs void?


Bajo qué escenarios uno querría usar

public async Task AsyncMethod(int num)

En lugar de

public async void AsyncMethod(int num)

El único escenario que se me ocurre es si necesitas la tarea para poder seguir su progreso.

Además, en el siguiente método, ¿son innecesarias las palabras clave async y await?

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}
Author: Boiethios, 2012-08-27

6 answers

1) Normalmente, querrías devolver un Task. La excepción principal debería ser cuando necesita tener un tipo de retorno void (para eventos). Si no hay razón para no permitir que el llamante await su tarea, ¿por qué no permitir que?

2) async los métodos que devuelven 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 es 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();
}

f'la excepción es siempre "observada". Una excepción que deja un método asíncrono de nivel superior simplemente se trata como cualquier otra excepción no controlada. la excepción de g nunca se observa. 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 esto sucede, se ejecuta el controlador TaskScheduler.UnobservedTaskException. Nunca deberías dejar que esto suceda. Para usar su ejemplo,

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

Sí, utilizar async y await aquí, se aseguran de que su método siga funcionando correctamente si se lanza una excepción.

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

 315
Author: suizo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-03 12:07:19

He encontrado 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 un async+void puede bloquear el sistema y generalmente debe usarse solo en los controladores de eventos secundarios de la interfaz de usuario.

La razón detrás de esto es el Contexto de sincronización utilizado por el AsyncVoidMethodBuilder, siendo none en este ejemplo. Cuando no hay ambiental Contexto de sincronización, cualquier excepción que el cuerpo de un método de vacío asincrónico se replantea en el ThreadPool. Mientras no hay aparentemente ningún otro lugar lógico donde ese tipo de excepción podría ser lanzado, el efecto desafortunado es que el proceso se está terminando, porque las excepciones no manejadas en el ThreadPool finalizar efectivamente el proceso desde. NET 2.0. Usted puede interceptar excepción no controlada con AppDomain.Evento UnhandledException, pero no hay forma de recuperar el proceso de este evento.

Al escribir controladores de eventos de interfaz de usuario, los métodos de vacío asincrónicos son de alguna manera indoloro porque las excepciones se tratan de la misma manera que se encuentran en métodos no asincrónicos; se lanzan al despachador. Hay un posibilidad de recuperarse de tales excepciones, con 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, async void los métodos son de alguna manera peligrosos de usar y pueden no ser tan fáciles de encontrar.

 27
Author: Davide Icardi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-11 15:40:55

Tengo una idea clara de estas declaraciones.

  1. Los métodos anulados asincrónicos tienen diferentes semánticas de manejo de errores. Cuando se elimina una excepción de una tarea asíncrona o de un método de tarea asíncrona, esa excepción se captura y se coloca en el objeto Task. Con los métodos async void, no hay objeto de tarea, por lo que cualquier excepción arrojada de un método async void se generará directamente en SynchronizationContext(SynchronizationContext representa una ubicación "donde" se podría ejecutar el código. ) eso fue activo cuando se inició el método void async

Las excepciones de un Método Nulo Asincrónico No Se pueden Detectar con Catch

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 no mantenerla (bloquea la aplicación).

  1. Los métodos de vacío asincrónicos tienen diferentes semánticas de composición. Métodos asincrónicos que devuelven Tarea o Tarea se puede componer fácilmente usando await, Task.Cuando sea, Tarea.WhenAll y así sucesivamente. Los métodos asincrónicos que devuelven void no proporcionan una manera fácil de notificar al código de llamada que han completado. Es fácil iniciar varios métodos de anulación asincrónica, pero no es fácil determinar cuándo han terminado. Los métodos anulados asincrónicos 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. Método Vacío asincrónico útil cuando se utiliza el controlador de eventos síncronos porque elevan sus excepciones directamente en el SynchronizationContext, que es similar a cómo se comportan los controladores de eventos síncronos

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

 14
Author: Nayas Subramanian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-25 09:07:33

Creo que también puedes usar async void para iniciar operaciones en segundo plano, siempre y cuando tengas cuidado de detectar 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());
        }
    }
}
 3
Author: bboyle1234,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-16 12:43:42

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

Estas son las tres formas de llamar a una función asincrónica:

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 finalmente produce la t.

En el segundo caso, la función devuelve una tarea que no tiene producto, pero puede todavía esperan en él para saber cuando se ha ejecutado a la finalización.

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

El caso vacío asincrónico es un " fuego y forget": Se inicia la cadena de tareas, pero no se preocupan por cuando es terminar. Cuando la función vuelve, todo lo que sabes es que todo hasta la primera espera ha ejecutado. Todo después de la primera espera se ejecutará en algún punto no especificado en el futuro que no tenga acceso a.

 2
Author: user8128167,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-19 15:31:19

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

Así que si el método es async es mejor que sea awaitable, porque puede perder la ventaja de async.

 0
Author: Serg Sh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-20 16:21:00