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.
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.
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.
WaitAll. The Task. WaitAll method waits for all of the provided tasks to complete execution. Program.cs.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With