Skip to Content
Course content

54: Cancellation Tokens

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Ever started a long-running task in an app—maybe a big file export or a database sync—and then realized you made a mistake and hit "Cancel," only to find that the app just ignored you? It's frustrating for the user and a waste of server resources. In C#, we handle this using CancellationToken and CancellationTokenSource.

To show you how this actually works, let's build a simple Log Analyzer. Imagine we're scanning a massive text file for a specific error keyword. Since the file could be gigabytes in size, we need a way to kill the process if the user decides they don't want to wait anymore.

Simulating a Heavy Scan

First, I'll create a method that simulates reading through a file. I'm using a simple loop here to represent the processing of lines. Notice how I'm passing a CancellationToken as a parameter. This is the standard pattern in .NET; almost every async method in the framework that takes a long time will have a token parameter.

public async Task SearchLogsAsync(string keyword, CancellationToken token)
{
    Console.WriteLine("Starting log scan...");
    
    for (int i = 1; i <= 100; i++)
    {
        // Simulate heavy work
        await Task.Delay(100, token); 
        Console.WriteLine($"Processing line {i}...");
    }
    
    Console.WriteLine("Scan complete!");
}

Wiring up the Cancellation Signal

The token itself is just a listener. To actually send the "stop" signal, we need a CancellationTokenSource (CTS). The CTS is the controller; the token is the messenger. I'll set up a simple console trigger to cancel the operation after a few seconds.

var cts = new CancellationTokenSource();

// Start the scan in the background
Task scanTask = SearchLogsAsync("CriticalError", cts.Token);

// Simulate the user clicking a "Cancel" button after 1 second
await Task.Delay(1000);
Console.WriteLine("User clicked cancel!");
cts.Cancel();

try 
{
    await scanTask;
}
catch (OperationCanceledException)
{
    Console.WriteLine("The scan was aborted successfully.");
}

The "Wait, it's still running!" Moment

Now, here is where I usually trip up when I'm rushing through a project. If I had a complex loop doing heavy CPU work—instead of just Task.Delay—the code above wouldn't actually stop immediately. I'll be honest: I've spent way too many hours debugging "zombie tasks" because I passed the token into the method but forgot to actually check it inside my logic.

If my loop was doing something like File.ReadAllLines() or heavy math, Task.Delay wouldn't be there to catch the cancellation. To fix this, I need to manually poll the token.

public async Task SearchLogsAsync(string keyword, CancellationToken token)
{
    for (int i = 1; i <= 100; i++)
    {
        // This is the missing piece! 
        // It throws OperationCanceledException if cts.Cancel() was called.
        token.ThrowIfCancellationRequested();

        // Imagine some heavy CPU-bound regex scanning happening here
        Thread.SpinWait(1000000); 
        
        Console.WriteLine($"Processing line {i}...");
    }
}

Cleaning up the Exception

You'll notice I wrapped the await scanTask in a try-catch block for OperationCanceledException. This is crucial. When you call ThrowIfCancellationRequested(), it doesn't just "return" from the method; it throws an exception to unwind the stack. If you don't catch it, your application will crash. In a real UI app, you'd catch this to hide the loading spinner and show a "Scan Cancelled" message to the user.

One last tip: always remember to dispose of your CancellationTokenSource. It uses a timer internally, and if you're creating thousands of these in a high-throughput system, you'll see a memory leak. Wrap it in a using block or call cts.Dispose() once you're done with the operation.




📋 Practical Task

Building a Cancellable Image Downloader

Your task is to create a program that simulates downloading three large images from a server. Each "download" should be a method that takes a CancellationToken and uses a loop to simulate progress (e.g., printing "Downloading image 1: 10%... 20%...").

  • Implement a CancellationTokenSource that triggers a cancellation after 2 seconds.
  • Ensure that the download methods check the token frequently using ThrowIfCancellationRequested().
  • Handle the resulting OperationCanceledException gracefully so the program prints "Download sequence aborted by user" instead of crashing.
  • Ensure the CancellationTokenSource is properly disposed.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.