Task.WaitAll throws OperationCanceledException [closed]

时光总嘲笑我的痴心妄想 提交于 2020-07-18 20:42:06

问题


I have a list of running tasks with the same CancellationTokenSource.

I want the current thread to wait until all the tasks complete or until the tasks were cancelled.

Task.WaitAll(tasks.ToArray(), searchCencellationTokenSource.Token);
System.Console.WriteLine("Done !");

The tasks might be cancelled by another task even when the current thread is in a waiting state. This is normal behavior.

However, while the current thread is in waiting state and another task cancel the tasks, the WaitAll throws CancellationTokenSource with a message: "The operation was canceled.".

I know it was cancelled, I did it intentionally. I just want it to continue to the next code after the tasks were cancelled or completed, without throwing an exception.

I know I can wrap this code with try & catch but throwing an exception is heavy operation and I don't want it to happen on a normal behavior like this.


回答1:


This blocking mechanism can be rephrased as:

Task.WhenAll(taskA, taskB, taskC).Wait()

This gives you a task back which we can await but can also manage cancellation from. So, to ignore cancellation exception, you can do the following:

Task.WhenAll(taskA, taskB, taskC).ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled).Wait();

Which won't throw an OperationCancelledException.

This could then be wrapped into an extension method as follows:

public static class TaskExtensions
{
    public static Task IgnoreCancellation(this Task task)
    {
        return task.ContinueWith(t => { }, TaskContinuationOptions.OnlyOnCanceled);
    }
}

Which would allow you to write the following, again without encountering an OperationCancelledException:

Task.WhenAll(taskA, taskB, taskC).IgnoreCancellation().Wait();

Here's a test fixture showing the approach working:

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

Hope it helps.



来源:https://stackoverflow.com/questions/40595720/task-waitall-throws-operationcanceledexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!