Cancellation Tokens in C#: Best Practices for .NET Core Applications

Cancellation Tokens in C#: Best Practices for .NET Core Applications

https://nilebits.com/blog/2024/06/cancellation-tokens-in-csharp/

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?

  1. Resource Management: Proper cancellation of tasks ensures that resources such as memory, file handles, and network connections are released promptly.
  2. Responsiveness: Applications can respond more swiftly to user actions or changing conditions by cancelling long-running tasks when they are no longer needed.
  3. Error Handling: Cancellation provides a clear pathway to handle tasks that should be stopped due to errors or other exceptional conditions.

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

  1. Pass Cancellation Tokens to All Async Methods: Ensure that all asynchronous methods accept a CancellationToken parameter, allowing for consistent cancellation throughout the call chain.

public async Task MethodA(CancellationToken token)
{
    await MethodB(token);
}

public async Task MethodB(CancellationToken token)
{
    await MethodC(token);
}        

  1. Check for Cancellation Periodically: Within long-running operations or loops, check the IsCancellationRequested property regularly to respond promptly to cancellation requests.

for (int i = 0; i < data.Length; i++)
{
    token.ThrowIfCancellationRequested();
    // Process data
}        

  1. Use ThrowIfCancellationRequested for Simplicity: Instead of manually checking IsCancellationRequested, use ThrowIfCancellationRequested to throw an OperationCanceledException automatically.

public async Task ProcessData(CancellationToken token)
{
    foreach (var item in data)
    {
        token.ThrowIfCancellationRequested();
        // Process item
    }
}        

  1. Graceful Cleanup: Ensure that resources are cleaned up properly when cancellation occurs. This includes disposing of any objects and rolling back partial operations if necessary.

public async Task ProcessFilesAsync(string[] files, CancellationToken token)
{
    foreach (string file in files)
    {
        token.ThrowIfCancellationRequested();
        // Process file
        await Task.Delay(1000); // Simulate work
    }
}        

  1. CancellationTokenSource Disposal: Always dispose of CancellationTokenSource instances to free resources.

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:

  • Always pass CancellationToken to asynchronous methods.
  • Check for cancellation regularly within long-running operations.
  • Use ThrowIfCancellationRequested for simpler code.
  • Dispose of CancellationTokenSource properly.
  • Consider linked cancellation tokens for complex scenarios.

By mastering these techniques, you can build high-performance, responsive applications that handle cancellation gracefully and efficiently.

https://nilebits.com/blog/2024/06/cancellation-tokens-in-csharp/

要查看或添加评论,请登录

社区洞察

其他会员也浏览了