Подтвердить что ты не робот

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

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

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

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

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

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

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

4b9b3361

Ответ 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);
            }
        }

    }
}