Home > Software engineering >  Throwing any exception from Task throws a TaskCanceledException
Throwing any exception from Task throws a TaskCanceledException

Time:01-23

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.

  • Related