I never call cts.Cancel()
, so why does it throw TaskCanceledException
exception?
var cts = new CancellationTokenSource();
try
{
await Task.Factory.StartNew(() =>
{
throw new Exception("");
}, cts.Token)
.ContinueWith(t =>
{
}, TaskContinuationOptions.OnlyOnCanceled);
}
catch (TaskCanceledException ex)
{
throw;
}
CodePudding user response:
It throws TaskCancelledException because of the Chained Task (task inside ContinueWith Method). Once you throw an exception inside your code, the task finishes with faulted result and starts the second task. When you use await (await Task.Factory ...) you await the SECOND task, which is cancelled because the previous task was failed (not cancelled), therefore, you get the task cancelled exception.
Await will throw any existing exception in the chain in to the calling thread. So you are getting the last exception in the chain (from the second task).
You can see in this example what code is executing in the test cases based on your code. Please note I removed the async keyword before ...StartNew(() => $ ...., and that CTS on Task.Run / Task.StartNew only prevents task for starting but wont stop the task unless you directly check for cancellation inside Task body.
Check the reference: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/
<pre>
var cases = new[] {
"Task normally run",
"Task with cancellation timedout before run",
"Task with cts timing out during run",
"Task with cancellation inside method (calling Cancel())"
};
var cancellationTimeout = new[] { 3000, 1, 500, 3000 };
var delay = new[] { 0, 500, 0, 0 };
var cancel = new[] { false, false, false, true };
for (int i = 0; i < cancellationTimeout.Length; i )
await test(i);
async Task test(int i)
{
Console.WriteLine($"Test case {i 1}. {cases[i]}");
Console.WriteLine($" Cancellation Timeout: {cancellationTimeout[i]} ms, \n Delay Start: {delay[i]} ms, \n Cancel task 1: {cancel[i]}, \n Task 1 duration: 750ms");
Console.WriteLine($"=== Console log ===");
var cts = new CancellationTokenSource(cancellationTimeout[i]);
try
{
await Task.Delay(delay[i]);
var t1 = Task.Factory.StartNew(() =>
{
Console.WriteLine(" Task 1 started");
if (cancel[i])
{
Console.WriteLine(" This is executed if the cancellation token is requested");
cts.Cancel();
Console.WriteLine(" This is executed when you check for cancellation");
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine(" This is executed after throw");
}
Task.Delay(500).Wait();
Console.WriteLine(" Task 1 finished");
}, cts.Token); //This canellation token only works if the current task has not started and cancellation request is .
var t2 = t1.ContinueWith(t =>
{
Console.WriteLine(" This is executed if the task 1 is cancelled and handled");
}, TaskContinuationOptions.OnlyOnCanceled);
Exception? Ex1 = null;
try
{
await t1;
}
catch (Exception ex) { Ex1 = ex; }
Exception? Ex2 = null;
try
{
await t2;
}
catch (Exception ex) { Ex2 = ex; }
Console.WriteLine($"=== Tasks ===");
Console.WriteLine($" Task 1 status:{t1.Status,20} Faulted?:{t1.IsFaulted,10} Ex:{Ex1?.Message}");
Console.WriteLine($" Task 2 status:{t2.Status,20} Faulted?:{t2.IsFaulted,10} Ex:{Ex2?.Message}");
Console.WriteLine("");
}
catch (TaskCanceledException ex)
{
throw;
}
}
</pre>
You can check the following results.
Test case 1. Task normally run
Cancellation Timeout: 3000 ms,
Delay Start: 0 ms,
Cancel task 1: False,
Task 1 duration: 750ms
=== Console log ===
Task 1 started
Task 1 finished
=== Tasks ===
Task 1 status: RanToCompletion Faulted?: False Ex:
Task 2 status: Canceled Faulted?: False Ex:A task was canceled.
Test case 2. Task with cancellation timedout before run
Cancellation Timeout: 1 ms,
Delay Start: 500 ms,
Cancel task 1: False,
Task 1 duration: 750ms
=== Console log ===
This is executed if the task 1 is cancelled and handled
=== Tasks ===
Task 1 status: Canceled Faulted?: False Ex:A task was canceled.
Task 2 status: RanToCompletion Faulted?: False Ex:
Test case 3. Task with cancellation timeout during run
Cancellation Timeout: 500 ms,
Delay Start: 0 ms,
Cancel task 1: False,
Task 1 duration: 750ms
=== Console log ===
Task 1 started
Task 1 finished
=== Tasks ===
Task 1 status: RanToCompletion Faulted?: False Ex:
Task 2 status: Canceled Faulted?: False Ex:A task was canceled.
Test case 4. Task with cancellation inside method
Cancellation Timeout: 3000 ms,
Delay Start: 0 ms,
Cancel task 1: True,
Task 1 duration: 750ms
=== Console log ===
Task 1 started
This is executed if the cancellation token is requested
This is executed when you check for cancellation
This is executed if the task 1 is cancelled and handled
=== Tasks ===
Task 1 status: Canceled Faulted?: False Ex:The operation was canceled.
Task 2 status: RanToCompletion Faulted?: False Ex:
Good Luck
CodePudding user response:
TaskCanceledException
is thrown because TaskContinuationOptions.OnlyOnCanceled
option is set for the continuation task, which causes the continuation task to throw a TaskCanceledException
if the antecedent task (the initial task) throws any exception, regardless of whether the antecedent task was actually canceled.