Cancellation Tokens in C#: Best Practices for .NET Core Applications
Amr Saafan
Founder | CTO | Software Architect & Consultant | Engineering Manager | Project Manager | Product Owner | +27K Followers | Now Hiring!
Introduction
With the help of Cancellation Tokens, a potent feature of the.NET environment, developers may gracefully terminate asynchronous processes. We will examine the best practices for utilizing Cancellation Tokens in.NET Core apps in this extensive tutorial. We will go over the fundamentals, explore more complex situations, and offer real-world examples to help clarify these ideas.
Asynchronous programming is a core idea in contemporary software development that improves the performance and responsiveness of applications. In situations like I/O-bound activities, network requests, or complicated calculations where tasks may take an unpredictable length of time to finish, asynchronous operations are essential. But asynchronous processes necessitate strong task management, especially when it comes to effectively and neatly handling job cancellations. This is where C#'s Cancellation Tokens are useful.
1. Understanding Cancellation Tokens
What are Cancellation Tokens?
Cancellation Tokens in C# provide a mechanism to cancel asynchronous operations. They work in conjunction with CancellationTokenSource to signal cancellation requests and CancellationToken to observe these requests. This cooperative model allows tasks to cancel themselves gracefully, ensuring proper resource management and avoiding potential issues such as memory leaks or incomplete operations.
Why Use Cancellation Tokens?
2. Basics of CancellationTokenSource and CancellationToken
CancellationTokenSource
The CancellationTokenSource class is responsible for signaling a cancellation request. It creates a CancellationToken that can be passed to tasks or operations that need to support cancellation.
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
// Signal cancellation
cts.Cancel();
CancellationToken
The CancellationToken struct provides a way to check for cancellation requests. Tasks or operations that accept a CancellationToken should periodically check its IsCancellationRequested property and terminate their work if a cancellation is requested.
void DoWork(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancellation requested.");
return;
}
// Simulate work
Thread.Sleep(1000);
}
}
3. Implementing Task Cancellation in .NET Core
Example: Basic Task Cancellation
Here's a basic example of how to implement task cancellation using CancellationToken.
public async Task DownloadFileAsync(string url, CancellationToken token)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(url, token);
response.EnsureSuccessStatusCode();
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine("File downloaded.");
}
}
public async Task Run()
{
CancellationTokenSource cts = new CancellationTokenSource();
try
{
Task downloadTask = DownloadFileAsync("https://example.com/file", cts.Token);
// Simulate user cancellation
cts.CancelAfter(3000); // Cancel after 3 seconds
await downloadTask;
}
catch (OperationCanceledException)
{
Console.WriteLine("Download cancelled.");
}
}
In this example, we create an HTTP client to download a file and pass the CancellationToken to the GetAsync method. If the download is not completed within 3 seconds, the operation is cancelled.
4. Handling Timeouts with Cancellation Tokens
Implementing Timeouts
You can use CancellationTokenSource to set timeouts for operations, ensuring they do not run indefinitely.
public async Task DownloadWithTimeoutAsync(string url, int timeout)
{
using (CancellationTokenSource cts = new CancellationTokenSource(timeout))
{
try
{
await DownloadFileAsync(url, cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Download timed out.");
}
}
}
public async Task Run()
{
await DownloadWithTimeoutAsync("https://example.com/file", 5000); // 5-second timeout
}
In this example, the download operation will be cancelled if it does not complete within 5 seconds.
5. Cooperative Cancellation in Asynchronous Programming
Example: Cooperative Cancellation
Cooperative cancellation involves checking the cancellation token periodically within a long-running operation.
领英推荐
public async Task ProcessDataAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Cancellation requested. Exiting...");
return;
}
// Simulate data processing
await Task.Delay(1000);
Console.WriteLine($"Processed chunk {i + 1}.");
}
}
public async Task Run()
{
CancellationTokenSource cts = new CancellationTokenSource();
Task processingTask = ProcessDataAsync(cts.Token);
// Simulate user cancellation
cts.CancelAfter(5000); // Cancel after 5 seconds
await processingTask;
}
In this example, ProcessDataAsync method processes data in chunks and checks the cancellation token periodically to decide whether to continue or exit.
6. Best Practices for Using Cancellation Tokens
public async Task MethodA(CancellationToken token)
{
await MethodB(token);
}
public async Task MethodB(CancellationToken token)
{
await MethodC(token);
}
for (int i = 0; i < data.Length; i++)
{
token.ThrowIfCancellationRequested();
// Process data
}
public async Task ProcessData(CancellationToken token)
{
foreach (var item in data)
{
token.ThrowIfCancellationRequested();
// Process item
}
}
public async Task ProcessFilesAsync(string[] files, CancellationToken token)
{
foreach (string file in files)
{
token.ThrowIfCancellationRequested();
// Process file
await Task.Delay(1000); // Simulate work
}
}
using (CancellationTokenSource cts = new CancellationTokenSource())
{
// Use cts.Token
}
7. Advanced Techniques and Patterns
Linked Cancellation Tokens
You can link multiple cancellation tokens together using CancellationTokenSource.CreateLinkedTokenSource. This is useful when you want to combine cancellation logic from different sources.
public async Task Run()
{
using (CancellationTokenSource cts1 = new CancellationTokenSource())
using (CancellationTokenSource cts2 = new CancellationTokenSource())
using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token))
{
Task task = SomeOperationAsync(linkedCts.Token);
// Cancel one of the tokens
cts1.Cancel();
try
{
await task;
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation cancelled via linked token.");
}
}
}
Composite Operations
When dealing with composite operations, ensure each sub-operation respects the cancellation token.
public async Task CompositeOperationAsync(CancellationToken token)
{
await OperationAAsync(token);
await OperationBAsync(token);
await OperationCAsync(token);
}
public async Task OperationAAsync(CancellationToken token) { /* ... */ }
public async Task OperationBAsync(CancellationToken token) { /* ... */ }
public async Task OperationCAsync(CancellationToken token) { /* ... */ }
8. Practical Examples in Real-World Applications
Example: Web API with Cancellation Tokens
In a real-world web API, you should use CancellationToken to cancel long-running requests. ASP.NET Core automatically passes a cancellation token to the controller action methods.
[ApiController]
[Route("api/[controller]")]
public class DataController : ControllerBase
{
private readonly IDataService _dataService;
public DataController(IDataService dataService)
{
_dataService = dataService;
}
[HttpGet("process")]
public async Task<IActionResult> ProcessData(CancellationToken token)
{
await _dataService.ProcessDataAsync(token);
return Ok("Data processed successfully.");
}
}
In this example, the ProcessData action method accepts a CancellationToken and passes it to the ProcessDataAsync method of the IDataService.
9. Conclusion
Cancellation Tokens are an essential part of modern C# programming, especially when dealing with asynchronous and long-running operations. They provide a standardized way to handle task cancellation, ensuring applications remain responsive and resource-efficient. By following the best practices outlined in this guide, you can implement robust and maintainable cancellation logic in your .NET Core applications.
Remember to:
By mastering these techniques, you can build high-performance, responsive applications that handle cancellation gracefully and efficiently.