Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Task.WaitAll throws OperationCanceledException [closed]

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.

like image 431
Jacob Avatar asked Nov 14 '16 18:11

Jacob


People also ask

Does task WaitAll block?

WhenAll we will get a task object that isn't complete. However, it will not block but will allow the program to execute. On the contrary, the Task. WaitAll method call actually blocks and waits for all other tasks to complete.

Which of the following is task WaitAll features?

The static Task. WaitAll() method is used to wait for a number of tasks to complete, so it will not return until all the given tasks will either complete, throw an exception or be cancelled.

What is task WaitAll in C#?

WaitAll. The Task. WaitAll method waits for all of the provided tasks to complete execution. Program.cs.


1 Answers

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.

like image 96
ibebbs Avatar answered Sep 25 '22 02:09

ibebbs