Saltar al contenido

async / await: ¿cuándo devolver una tarea frente a un vacío?

Solución:

1) Normalmente, querrá devolver un Task. La principal excepción debería ser cuando necesitar tener un void tipo de retorno (para eventos). Si no hay razón para no permitir que la persona que llama await tu tarea, ¿por qué rechazarla?

2) async métodos que regresan void son especiales en otro aspecto: representan operaciones asincrónicas de nivel superiory tiene reglas adicionales que entran en juego cuando su tarea devuelve una excepción. La forma más sencilla 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();
}

fLa excepción de siempre es “observada”. Una excepción que deja un método asincrónico de nivel superior simplemente se trata como cualquier otra excepción no controlada. gLa excepción de 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 eso sucede, el TaskScheduler.UnobservedTaskException corre el manejador. Nunca debes dejar que esto suceda. Para usar tu ejemplo,

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

Si, usa 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

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

La conclusión es que un async+void puede bloquear el sistema y, por lo general, solo debe usarse 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 AsyncVoidMethodBuilder, que no es ninguno en este ejemplo. Cuando no hay un contexto de sincronización ambiental, cualquier excepción que no esté controlada por el cuerpo de un método void asincrónico se vuelve a generar en ThreadPool. Si bien aparentemente no hay otro lugar lógico donde se pueda lanzar ese tipo de excepción no controlada, el efecto desafortunado es que el proceso se está terminando, porque las excepciones no controladas en ThreadPool terminan efectivamente el proceso desde .NET 2.0. Puede interceptar todas las excepciones no controladas mediante 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 async; se lanzan al Dispatcher. Existe la posibilidad de recuperarse de tales excepciones, lo que 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 de vacío asíncronos son de alguna manera peligrosos de usar y es posible que no sean tan fáciles de encontrar.

Tengo una idea clara de estas declaraciones.

  1. Los métodos Async void tienen una semántica de manejo de errores diferente. Cuando se arroja una excepción de una Tarea asíncrona o un método Tarea asíncrona, esa excepción se captura y se coloca en el objeto Tarea. Con los métodos void asíncronos, no hay un objeto Task, por lo que cualquier excepción lanzada fuera de un método void asíncrono se generará directamente en SynchronizationContext (SynchronizationContext representa una ubicación “donde” se podría ejecutar el código) que estaba activo cuando el método void asíncrono empezado

Las excepciones de un método Async Void 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 general similar para aplicaciones GUI / ASP.NET, pero usar esos eventos para el manejo regular de excepciones es una receta para la falta de mantenimiento (bloquea la aplicación).

  1. Los métodos Async void tienen diferentes semánticas de composición. Los métodos asincrónicos que devuelven Task o Task se pueden componer fácilmente usando await, Task.WhenAny, Task.WhenAll y así sucesivamente. Los métodos asíncronos que devuelven vacío no proporcionan una manera fácil de notificar al código de llamada que han completado. Es fácil iniciar varios métodos de vacío asíncronos, pero no es fácil determinar cuándo han terminado. Los métodos 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 normal.

  2. El método Async Void es útil cuando se usa el controlador de eventos síncronos porque generan 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

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *