Как я могу ждать выполнения задач, не выбрасывая TaskCanceledExceptions?

У меня есть метод, который создает некоторые задачи, а затем ожидает их выполнения с помощью WaitAll, прежде чем вернуться. Проблема в том, что если эти задачи были отменены, то WaitAll выдает AggregateException, содержащую множество TaskCanceledException s.

Это означает, что WaitAll будет вызывать исключения в двух разных случаях:

  • Исключения, указывающие на настоящую ошибку. Это означает, что возникло состояние, с которым мы не знали, как справиться; они должны распространяться как необработанные исключения, пока они в конечном итоге не завершат процесс.
  • Исключения, указывающие на то, что пользователь нажал кнопку "Отмена". Это означает, что задача была отменена и очищена, и программа должна продолжить работу в обычном режиме.

Последнее полностью вписывается в определение досадного исключения: это исключение, возникшее в совершенно неисключительных обстоятельствах, поэтому я должен его перехватить, чтобы возобновить нормальный поток управления. К счастью, поймать его легко, правда? Просто добавьте catch (AggregateException) и - подождите, это тот же тип, который выдается при фатальной ошибке.

Мне нужно дождаться завершения выполнения задач, прежде чем я вернусь (мне нужно знать, что они больше не используют свои подключения к базе данных, дескрипторы файлов или что-то еще), поэтому мне нужен WaitAll или что-то подобное. И если какая-либо из задач не удалась, я действительно хочу, чтобы эти исключения распространялись как необработанные исключения. Я просто не хочу исключений для отмены.

Как я могу предотвратить WaitAll создание исключений для отмененных задач?


person Joe White    schedule 30.12.2011    source источник
comment
Используйте перегрузку, которая принимает CancellationToken.   -  person Hans Passant    schedule 30.12.2011
comment
@HansPassant, документы для этой перегрузки на самом деле не говорят, для чего он использует токен. Будет ли он игнорировать TaskCanceledExceptions, связанный с этим токеном, или он просто вернется раньше, если токен будет отменен? Мне нужно, чтобы он не возвращался, пока не остановятся все задачи.   -  person Joe White    schedule 30.12.2011
comment
Вы используете CancellationToken для отмены задач. И отфильтруйте конкретное исключение OperationCancelException, которое вы теперь получите.   -  person Hans Passant    schedule 30.12.2011


Ответы (2)


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, содержащий только необработанные исключения.

person João Angelo    schedule 30.12.2011

На основе предложения Жоао Анджело, здесь идет расширение класса Task

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);
            }
        }

    }
}
person Julio Nobre    schedule 22.10.2014