Как я могу ждать выполнения задач, не бросая TaskCanceledExceptions?
У меня есть метод, который создает некоторые Задачи, а затем ждет их с помощью WaitAll перед возвратом. Проблема в том, что если эти задачи были отменены, WaitAll выбрасывает AggregateException, содержащий много TaskCanceledException s.
Это означает, что WaitAll будет генерировать исключения в двух разных обстоятельствах:
- Исключения, которые указывают на подлинную ошибку. Это означает, что было условие, которое мы не знали, как обращаться; они должны распространяться как необработанные исключения, пока они в конечном итоге не прекратят процесс.
- Исключения, указывающие, что пользователь нажал кнопку "Отмена". Это означает, что задача была отменена и очищена, и программа должна продолжать работать нормально.
Последнее вписывается в определение досадного исключения: это исключение, возникшее в совершенно не исключительных обстоятельствах, поэтому я должен поймать его, чтобы возобновить нормальный поток управления. К счастью, легко поймать, не так ли? Просто добавьте catch (AggregateException)
и - о, подождите, что тот же тип, который бросается, когда возникает фатальная ошибка.
Мне нужно дождаться завершения задач до того, как я вернусь (мне нужно знать, что они больше не используют свои подключения к базе данных, дескрипторы файлов или что-то еще), поэтому мне нужен WaitAll или что-то похожее, И если какая-либо из задач не работает, я хочу, чтобы эти исключения распространялись как необработанные исключения. Я просто не хочу исключений для отмены.
Как я могу предотвратить WaitAll
отбрасывания исключений для отмененных задач?
Ответы
Ответ 1
AggregateException
предоставляет метод Handle
, который может использоваться для этих ситуаций. Если, например, вы хотите игнорировать TaskCanceledException
, вы можете сделать:
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);
}
Если все исключения имеют тип TaskCanceledException
, метод Handle
не будет вызывать никаких исключений; в противном случае будет создан новый AggregateException
, содержащий только необработанные исключения.
Ответ 2
На основе Предложения João Angelo, здесь добавлено расширение Задача
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 stackoverflow suggestion /info/225116/how-can-i-wait-on-tasks-without-throwing-taskcanceledexceptions/1192002#1192002
// 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);
}
}
}
}