Task.WaitAll создает исключение OperationCanceledException
У меня есть список запущенных задач с таким же CancellationTokenSource
,
Я хочу, чтобы текущий поток ждал, пока все задачи не будут выполнены или пока задачи не будут отменены.
Task.WaitAll(tasks.ToArray(), searchCencellationTokenSource.Token);
System.Console.WriteLine("Done !");
Задачи могут быть отменены другой задачей, даже если текущий поток находится в состоянии ожидания. Это нормальное поведение.
Однако, пока текущий поток находится в состоянии ожидания и другая задача отменяет задачи, WaitAll выбрасывает CancellationTokenSource
с сообщением: "Операция была отменена".
Я знаю, что это было отменено, я сделал это намеренно. Я просто хочу перейти к следующему коду после того, как задачи были отменены или завершены, без исключения.
Я знаю, что могу обернуть этот код с помощью try & catch, но создание исключения - тяжелая операция, и я не хочу, чтобы это происходило при нормальном поведении, подобном этому.
1 ответ
Этот механизм блокировки можно перефразировать как:
Task.WhenAll(taskA, taskB, taskC).Wait()
Это дает вам задание, которое мы можем ожидать, но также можем отменить отмену. Таким образом, чтобы игнорировать исключение отмены, вы можете сделать следующее:
Task.WhenAll(taskA, taskB, taskC).ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled).Wait();
Который не бросит OperationCancelledException
,
Затем его можно включить в метод расширения следующим образом:
public static class TaskExtensions
{
public static Task IgnoreCancellation(this Task task)
{
return task.ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled);
}
}
Что позволит вам написать следующее, опять же, не встречая OperationCancelledException
:
Task.WhenAll(taskA, taskB, taskC).IgnoreCancellation().Wait();
Вот тестовое приспособление, показывающее работающий подход:
public class IgnoreTaskCancellation
{
[Fact]
public void ShouldThrowAnAggregateException()
{
CancellationTokenSource cts = new CancellationTokenSource(10);
Task taskA = Task.Delay(20, cts.Token);
Task taskB = Task.Delay(20, cts.Token);
Task taskC = Task.Delay(20, cts.Token);
Assert.Throws<AggregateException>(() => Task.WhenAll(taskA, taskB, taskC).Wait());
}
[Fact]
public void ShouldNotThrowAnException()
{
CancellationTokenSource cts = new CancellationTokenSource(10);
Task taskA = Task.Delay(20, cts.Token);
Task taskB = Task.Delay(20, cts.Token);
Task taskC = Task.Delay(20, cts.Token);
Task.WhenAll(taskA, taskB, taskC).ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled).Wait();
}
[Fact]
public void ShouldNotThrowAnExceptionUsingIgnore()
{
CancellationTokenSource cts = new CancellationTokenSource(10);
Task taskA = Task.Delay(20, cts.Token);
Task taskB = Task.Delay(20, cts.Token);
Task taskC = Task.Delay(20, cts.Token);
Task.WhenAll(taskA, taskB, taskC).IgnoreCancellation().Wait();
}
}
Надеюсь, поможет.