Как я могу ждать на задачах, не выбрасывая TaskCanceledExceptions? - PullRequest
23 голосов
/ 30 декабря 2011

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

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

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

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

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

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

Ответы [ 2 ]

28 голосов
/ 30 декабря 2011

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

1 голос
/ 22 октября 2014

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

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

    }
}
...