When to Use ThrowIfCancellationRequested() in C#

Cancellation is an essential part of modern asynchronous programming in C#. It allows applications to stop work that is no longer needed, improving responsiveness, reducing resource consumption, and preventing unnecessary processing.

One of the most commonly used methods for handling cancellation is CancellationToken.ThrowIfCancellationRequested(). While the method itself is simple, many developers are unsure about when it should be used and where it should be placed within their code.

In this article, we'll explore what hrowIfCancellationRequested() does, when you should use it, and some best practices for implementing cancellation effectively.

What Is ThrowIfCancellationRequested()?

ThrowIfCancellationRequested() is a method provided by the CancellationToken structure in C# that helps an operation respond to cancellation requests in a standard way. When this method is called, it checks whether cancellation has been requested through the associated CancellationToken.

If cancellation has been requested, it immediately throws an OperationCanceledException otherwise, execution continues normally. This method is commonly used in long-running or asynchronous operations to stop processing gracefully when a user, application, or system requests cancellation.

Instead of manually checking the IsCancellationRequested property and throwing an exception yourself, ThrowIfCancellationRequested() provides a concise and recommended approach that integrates properly with the .NET cancellation framework, making it easier for callers to detect and handle canceled operations.

Internally, it is equivalent to:

if (cancellationToken.IsCancellationRequested)
{
  throw new OperationCanceledException(cancellationToken);
}

Why Is It Important?

Consider a scenario where a user starts a long-running operation, such as:
  • Processing thousands of records
  • Downloading large files
  • Generating reports
  • Running background jobs

If the user decides to cancel the operation, your code should stop as quickly as possible.

Without cancellation checks, the operation may continue running unnecessarily, wasting CPU time, memory, network bandwidth, and database resources.

How ThrowIfCancellationRequested() Works

ThrowIfCancellationRequested() works by examining the state of a CancellationToken to determine whether a cancellation request has been made. When a CancellationTokenSource calls its Cancel() method, all associated CancellationToken instances are marked as canceled.

The next time ThrowIfCancellationRequested() is executed, it checks the token's IsCancellationRequested property. If the property is true, the method throws an OperationCanceledException that contains the cancellation token, allowing the cancellation to propagate through the call stack and be handled appropriately by the caller.

If no cancellation has been requested, the method does nothing and the operation continues normally. This mechanism enables cooperative cancellation, where running tasks periodically check for cancellation requests and terminate gracefully instead of being forcibly stopped.

When Should You Use ThrowIfCancellationRequested()?

  1. Inside Long-Running Loops

    This is the most common use case. Because cancellation can occur at any point while the loop is executing. By checking regularly, the application can stop promptly.

    
    foreach (var item in items)
    {
      cancellationToken.ThrowIfCancellationRequested();
      ProcessItem(item);
    }
    
  2. Before Expensive Operations

    If you're about to start a costly task, check for cancellation first. This prevents expensive work from starting when cancellation has already been requested.

    
    cancellationToken.ThrowIfCancellationRequested();
    var report = GenerateLargeReport();
    
  3. Between Processing Stages

    For workflows that contain multiple phases. This allows cancellation to interrupt the process between major steps.

    
    LoadData(); 
    
    cancellationToken.ThrowIfCancellationRequested(); 
    
    TransformData(); 
    
    cancellationToken.ThrowIfCancellationRequested(); 
    
    SaveResults();
    
  4. In Custom Async Methods

    When creating reusable async APIs, it's good practice to respect the provided cancellation token. This makes your API cancellation-friendly.

    
    public async Task ProcessOrdersAsync( CancellationToken cancellationToken) 
    { 
    	cancellationToken.ThrowIfCancellationRequested(); 
    	await LoadOrdersAsync(); 
    	
    	cancellationToken.ThrowIfCancellationRequested(); 
    	
    	await ValidateOrdersAsync(); cancellationToken.ThrowIfCancellationRequested(); 
    	
    	await SaveOrdersAsync(); 
    }
    
  5. Use it after the query if you're doing additional processing
    
    var users = await dbContext.Users
    .ToListAsync(cancellationToken);
    cancellationToken.ThrowIfCancellationRequested();
    var report = ProcessUsers(users);
    

    The query may have completed successfully, but the caller may no longer care about the expensive processing that follows.

  6. ASP.NET Core example
    
    [HttpGet]
    public async Task<IActionResult> GetUsers(
    CancellationToken cancellationToken)
    {
      var users = await dbContext.Users
      .ToListAsync(cancellationToken);
      return Ok(users);
    }
    

    Here, the framework provides a token tied to the HTTP request. If the client disconnects or the request is aborted, EF Core can cancel the query. An explicit ThrowIfCancellationRequested() is usually unnecessary unless you have additional work before or after the query.


Using ThrowIfCancellationRequested() with Database Queries

When working with database operations, especially queries that may take several seconds to complete, it's important to respect cancellation requests so that unnecessary database work can be stopped when a user cancels an operation or disconnects from an application.


public async Task<List<Customer>> GetCustomersAsync(
CancellationToken cancellationToken)
{
  // Check for cancellation before starting the query
  cancellationToken.ThrowIfCancellationRequested();
  using var context = new AppDbContext();
  var customers = await context.Customers
  .Where(c => c.IsActive)
  .ToListAsync(cancellationToken);
  // Check again before performing additional processing
  cancellationToken.ThrowIfCancellationRequested();
  return customers;
}

In this example, ThrowIfCancellationRequested() is called before the database query begins to avoid executing the query if cancellation has already been requested. The CancellationToken is also passed to ToListAsync(), allowing Entity Framework Core to cancel the database operation while it is running. After the query completes, another cancellation check is performed before any further processing occurs.

This approach ensures that the application responds quickly to cancellation requests and avoids wasting database and application resources on work that is no longer needed.

Common Mistakes to Avoid

ThrowIfCancellationRequested() is a useful method on a CancellationToken, but it's often misunderstood or misused. Here are some common mistakes to avoid when using it in .NET

Calling It Too Infrequently

If your operation performs a long-running loop or computation, checking cancellation only at the beginning means cancellation requests won't be honored promptly.


foreach (var item in items)
{
  token.ThrowIfCancellationRequested();
  ProcessItem(item);
}

Instead of:

token.ThrowIfCancellationRequested();

foreach (var item in items)
{
    ProcessItem(item); // May run for a long time
}

Calling It Excessively in Performance-Critical Code

While the method is lightweight, checking on every single iteration of a very tight loop can add overhead.


for (int i = 0; i < 1000000000; i++)
{
    if (i % 1000 == 0)
        token.ThrowIfCancellationRequested();

    Compute();
}

Balance responsiveness with performance.

Swallowing OperationCanceledExceptio

A common mistake is catching and ignoring the exception generated by ThrowIfCancellationRequested().


try
{
    token.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
{
    // Bad: cancellation is hidden
}

Unless you have a specific reason, let it propagate so callers know the operation was canceled.

Throwing After Completing Important Work

Cancellation should generally be checked before starting expensive work, not after it's already done.

token.ThrowIfCancellationRequested();

await SaveToDatabaseAsync();


Not:

await SaveToDatabaseAsync();

token.ThrowIfCancellationRequested();


Once side effects are committed, cancellation may no longer make sense.

Using It Instead of Passing the Token to Async APIs

Bad:

token.ThrowIfCancellationRequested();
await Task.Delay(5000);


Better:

await Task.Delay(5000, token);


Passing the token allows the API itself to stop work immediately.

Forgetting Cancellation Checks in Custom Async Work

If you're writing your own async methods with multiple steps, periodically check the token.


public async Task ProcessAsync(CancellationToken token)
{
    token.ThrowIfCancellationRequested();

    await Step1Async(token);

    token.ThrowIfCancellationRequested();

    await Step2Async(token);
}


Treating Cancellation as an Error

OperationCanceledException is usually a normal control-flow signal, not a failure.


try
{
    await DoWorkAsync(token);
}
catch (OperationCanceledException)
{
    // Expected cancellation
}


Avoid logging it as a critical error unless cancellation was unexpected.

Creating Inconsistent State Before Throwing

Be careful when cancellation can occur between related operations.


account.Withdraw(amount);

token.ThrowIfCancellationRequested();

account.LogTransaction();


If cancellation happens after the withdrawal but before logging, the system may end up inconsistent. Consider making such operations atomic or defining a "point of no cancellation."

Using It When IsCancellationRequested Is Sufficient

Sometimes you don't want an exception—you just want to exit gracefully.


if (token.IsCancellationRequested)
{
    return;
}


Use ThrowIfCancellationRequested() when cancellation should propagate to callers as an OperationCanceledException.

Summary

ThrowIfCancellationRequested() is a simple but powerful method that helps make applications responsive and resource-efficient. Use it when performing long-running work, inside loops, before expensive operations, and between processing stages.

As a general rule:
  • Use it where cancellation needs to be observed.
  • Skip it for trivial operations.
  • Prefer it over manually checking IsCancellationRequested.
  • Always propagate cancellation tokens through your call stack.

By following these practices, you'll build C# applications that handle cancellation correctly and provide a better experience for users and systems alike.

Thanks

Kailash Chandra Behera

I am an IT professional with over 13 years of experience in the full software development life cycle for Windows, services, and web-based applications using Microsoft .NET technologies.

Previous Post Next Post

نموذج الاتصال