CancellationTokenSource
async programming
.NET
task cancellation
asynchronous methods

Why is the task is not cancelled when I call CancellationTokenSource's Cancel method in async method?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In .NET, cancellation is cooperative, not forced. Calling Cancel on a CancellationTokenSource only signals intent to cancel; the running operation must observe that token and stop itself. If your task continues running after Cancel, the code path is usually not checking the token or is awaiting APIs that ignore it.

Cancellation Is a Signal, Not a Kill Switch

A token has two roles:

  • Producer side calls Cancel.
  • Consumer side checks token and exits quickly.

If consumer code never checks token state, no cancellation happens.

csharp
1using System;
2using System.Threading;
3using System.Threading.Tasks;
4
5static async Task<int> WorkAsync(CancellationToken token)
6{
7    for (int i = 0; i < 20; i++)
8    {
9        token.ThrowIfCancellationRequested();
10        await Task.Delay(100, token);
11    }
12    return 42;
13}
14
15using var cts = new CancellationTokenSource();
16var task = WorkAsync(cts.Token);
17cts.CancelAfter(350);
18
19try
20{
21    Console.WriteLine(await task);
22}
23catch (OperationCanceledException)
24{
25    Console.WriteLine("cancelled");
26}

This works because both loop and delay honor the token.

Why Cancel Seems Ignored

Common causes:

  • Token was never passed into the async method.
  • Method accepted token but never checked it.
  • Underlying API call had no token overload.
  • Work is CPU-bound loop with no cancellation checkpoint.
  • Task already completed before cancel request.

A token can only affect code that cooperates.

CPU-Bound Work Must Check Explicitly

For CPU-heavy loops, insert periodic cancellation checks.

csharp
1static Task<long> SumAsync(int n, CancellationToken token)
2{
3    return Task.Run(() =>
4    {
5        long sum = 0;
6        for (int i = 0; i < n; i++)
7        {
8            if ((i & 1023) == 0)
9                token.ThrowIfCancellationRequested();
10            sum += i;
11        }
12        return sum;
13    }, token);
14}

Without checkpoints, cancellation appears delayed or ineffective.

I O Calls Need Token-Aware APIs

If an awaited operation has a token overload, use it.

csharp
1using System.Net.Http;
2
3static async Task<string> FetchAsync(HttpClient client, string url, CancellationToken token)
4{
5    using var response = await client.GetAsync(url, token);
6    return await response.Content.ReadAsStringAsync(token);
7}

If the API lacks token support, cancellation may only occur between operations, not during the blocking call.

Correct Task State Expectations

After cooperative cancellation:

  • Awaiting usually throws OperationCanceledException.
  • Task status becomes canceled, not faulted.

If you catch and swallow OperationCanceledException, caller may think operation completed normally. Preserve cancellation semantics unless business logic requires conversion.

csharp
1catch (OperationCanceledException)
2{
3    throw;
4}

Linked Tokens and Timeout Policies

Real services often combine user cancellation and timeout cancellation.

csharp
1using var userCts = new CancellationTokenSource();
2using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
3using var linked = CancellationTokenSource.CreateLinkedTokenSource(
4    userCts.Token,
5    timeoutCts.Token
6);
7
8await WorkAsync(linked.Token);

This gives one cancellation path while keeping source intent separable.

Debugging Checklist

When task is not canceled as expected:

  1. Confirm the token instance passed to worker is the same one canceled.
  2. Confirm awaited APIs receive the token.
  3. Confirm loops call ThrowIfCancellationRequested or similar checks.
  4. Confirm cancellation is not swallowed by broad catch blocks.
  5. Confirm operation was not already complete before cancel signal.

These checks usually isolate the issue quickly.

Common Pitfalls

  • Treating Cancel as thread abort behavior.
  • Creating a token but forgetting to pass it through call chains.
  • Calling token-aware methods without token overloads.
  • Swallowing cancellation exceptions and returning success.
  • Missing checkpoints in long CPU-bound loops.

Summary

  • .NET task cancellation is cooperative and requires explicit observation.
  • Cancel only signals intent; worker code must honor it.
  • Pass tokens end-to-end and use token-aware API overloads.
  • Add cancellation checkpoints in CPU-intensive logic.
  • Preserve cancellation semantics so callers can respond correctly.

Course illustration
Course illustration

All Rights Reserved.