exception handling - wonder - ¿Cómo puedo esperar en las tareas sin lanzar TaskCanceledExceptions?
wunderlist español (2)
Basado en la sugerencia de João Angelo , aquí va una extensión de clase de tarea
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MySharedLibrary.Extensions
{
public static class TaskExtensions
{
// This code is based João Angelo''s stackoverflow suggestion https://stackoverflow.com/a/8681687/378115
// Use this when a CancellationTokenSource is used
public static void SafeWait(this Task TargetTask, CancellationTokenSource TargetTaskCancellationTokenSource)
{
if (TargetTaskCancellationTokenSource.IsCancellationRequested == false)
{
TargetTaskCancellationTokenSource.Cancel();
}
SafeWait(TargetTask);
}
// Use this when no CancellationTokenSource is used
public static void SafeWait(this Task TargetTask)
{
try
{
if (TargetTask.IsCanceled == false)
{
TargetTask.Wait();
}
}
catch (AggregateException errors)
{
errors.Handle(e => e is TaskCanceledException);
}
}
}
}
Tengo un método que crea algunas tareas y luego las espera con WaitAll antes de regresar. El problema es que, si esas tareas se cancelaron, WaitAll lanzará una excepción AggregateException contiene muchas TaskCanceledException s
Eso significa que WaitAll lanzará excepciones en dos circunstancias diferentes:
- Excepciones que indican un error genuino. Esto significa que hubo una condición que no sabíamos cómo manejar; necesitan propagarse como excepciones no manejadas, hasta que finalmente terminen el proceso.
- Excepciones que indican que el usuario hizo clic en el botón Cancelar. Esto significa que la tarea se canceló y se limpió, y el programa debería continuar ejecutándose normalmente.
Este último encaja perfectamente en la definición de una excepción molesta : es una excepción lanzada en una circunstancia completamente no excepcional, por lo que tengo que atraparla para reanudar el flujo de control normal. Afortunadamente es fácil de atrapar, ¿verdad? Simplemente agregue catch (AggregateException)
y - oh, espere, ese es el mismo tipo que se produce cuando hay un error fatal.
Necesito esperar a que las tareas terminen de ejecutarse antes de volver (necesito saber que ya no están usando sus conexiones de base de datos, controladores de archivos o cualquier otra cosa), por lo que necesito el WaitAll o algo similar. Y si alguna de las tareas falla, quiero que esas excepciones se propaguen como excepciones no manejadas. Simplemente no quiero excepciones para cancelar.
¿Cómo puedo evitar que WaitAll
lance excepciones para las tareas canceladas?
La AggregateException
proporciona un método de Handle
que se puede usar para estas situaciones. Si, por ejemplo, quiere ignorar la TaskCanceledException
, puede hacerlo:
var all = new AggregateException(
new NullReferenceException(),
new TaskCanceledException(),
new TaskCanceledException(),
new InvalidOperationException(),
new TaskCanceledException());
try
{
throw all;
}
catch (AggregateException errors)
{
errors.Handle(e => e is TaskCanceledException);
}
Si todas las excepciones son del tipo TaskCanceledException
, el método Handle
no generará ninguna excepción; de lo contrario, se lanzará una nueva excepción AggregateException
contiene solo las excepciones no manejadas.