Asp.Net Core Using Tasks And Cancellation Tokens Complete Guide

 Last Update:2025-06-23T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    8 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of ASP.NET Core Using Tasks and Cancellation Tokens

ASP.NET Core Using Tasks and Cancellation Tokens

Understanding Tasks

In .NET, a Task represents an asynchronous operation. It enables you to perform work asynchronously and handle the result once the operation is completed. Using Task in ASP.NET Core is essential for handling I/O-bound and long-running operations without blocking the main thread.

Creating and Running Tasks

In ASP.NET Core, tasks can be initiated using the Task.Run or Task.Factory.StartNew methods. Functions like HttpClient.SendAsync, DbContext.SaveChangesAsync, and file I/O operations are typically performed asynchronously using tasks. Here’s an example of running an asynchronous task:

public async Task PerformOperationAsync()
{
    await Task.Run(() =>
    {
        // Simulate an IO operation
        Thread.Sleep(3000);
        // Perform some operation here
    });
}

In the example above, Task.Run is used to offload the operation to a background thread, preventing the main thread from becoming blocked.

Async/Await Pattern

The async and await keywords simplify writing asynchronous code. They transform regular synchronous methods into asynchronous methods that can be non-blocking. Using async/await helps in readability and maintainability:

public async Task<string> FetchDataAsync()
{
    var httpClient = new HttpClient();
    return await httpClient.GetStringAsync("https://api.example.com/data");
}

Here, GetStringAsync is called asynchronously using await, and the method itself is declared as async to enable the use of await.

Cancellation Tokens

A CancellationToken is used to communicate cancellation requests to the method performing the operation. This is important for scenarios where the operation may be lengthy and you need the ability to cancel it. Here’s how to use a CancellationToken:

public async Task PerformCancelableOperationAsync(CancellationToken cancellationToken)
{
    using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
    {
        cts.CancelAfter(5000); // Cancel after 5 seconds

        try
        {
            await Task.Delay(10000, cts.Token); // Simulate a long-running operation
        }
        catch (OperationCanceledException)
        {
            // Handle cancellation
        }
    }
}

In this code:

  • A CancellationTokenSource is created linked to the provided cancellationToken.
  • CancelAfter schedules the cancellation to occur after a specified delay.
  • Task.Delay simulates a long-running operation.
  • An OperationCanceledException is caught and handled if the operation is canceled.

Important Use Cases

  • Web APIs: For long-running web API calls, cancellation tokens ensure that client aborts can be respected, preventing unnecessary processing.
  • Background Services: In background worker services, tasks can be canceled gracefully, reducing resource consumption.
  • File Operations: Large file upload or download operations can be canceled if the user decides to abort.

Key Important Info

  • Thread Safety: Ensure that shared resources accessed by multiple tasks are thread-safe.
  • Exception Handling: Properly handle exceptions within tasks to prevent unhandled exceptions propagating.
  • Resource Cleanup: Use finally blocks or using statements to ensure that resources are cleaned up correctly.
  • Performance Considerations: Avoid unnecessary synchronous operations, as they can block threads and degrade performance.

Conclusion

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement ASP.NET Core Using Tasks and Cancellation Tokens

Prerequisites

  • Basic knowledge of ASP.NET Core.
  • Basic understanding of C# and asynchronous programming.

Example Overview

In this example, we will create an ASP.NET Core web application that:

  1. Starts a long-running task using Task.Run when the user navigates to the /Process endpoint.
  2. Allows the user to cancel this task via the /Cancel endpoint.

Steps

Step 1: Create an ASP.NET Core Web Application

First, let's create a new ASP.NET Core Web API project.

Command Line (dotnet)

dotnet new webapi -n AspNetCoreTasksAndCancellationTokenExample
cd AspNetCoreTasksAndCancellationTokenExample

Step 2: Define a Background Service

We will create a service that manages our long-running task and its cancellation token.

  1. Create a BackgroundService.cs file in the Services folder inside your project.

Services/BackgroundService.cs

using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

public class BackgroundService
{
    private readonly ILogger<BackgroundService> _logger;
    private CancellationTokenSource _cancellationTokenSource;
    private Task _backgroundTask;

    public BackgroundService(ILogger<BackgroundService> logger)
    {
        _logger = logger;
        _cancellationTokenSource = new CancellationTokenSource();
    }

    public void StartProcessing()
    {
        if (_backgroundTask != null && _backgroundTask.Status == TaskStatus.Running)
        {
            _logger.LogInformation("Task is already running.");
            return;
        }
        
        _cancellationTokenSource = new CancellationTokenSource();
        _backgroundTask = Task.Run(AsyncLongRunningProcess, _cancellationTokenSource.Token);
        _logger.LogInformation("Process started.");
    }

    private async Task AsyncLongRunningProcess()
    {
        try
        {
            var cancellationToken = _cancellationTokenSource.Token;
            for (var i = 0; i < 100; i++)
            {
                cancellationToken.ThrowIfCancellationRequested();

                // Simulate work with Delay and LogInformation
                await Task.Delay(200, cancellationToken);
                _logger.LogInformation($"Processing iteration {i + 1}.");
            }

            _logger.LogInformation("Process completed successfully.");
        }
        catch (OperationCanceledException ex)
        {
            _logger.LogWarning(ex, "Process was cancelled.");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An error occurred during the process.");
        }
    }

    public void CancelProcessing()
    {
        if (_backgroundTask == null || _backgroundTask.Status != TaskStatus.Running)
        {
            _logger.LogInformation("No running task to cancel.");
            return;
        }

        _cancellationTokenSource.Cancel();
        // Optionally, wait the task to be cancelled.
        try
        {
            _backgroundTask.Wait();
        }
        catch (AggregateException)
        {
            // Expected exception from the canceled task
            _logger.LogInformation("Waited for task to be cancelled.");
        }

        _logger.LogInformation("Process cancelled.");
    }
}

Step 3: Register the Service in Startup.cs

Next, let's register our service with ASP.NET Core dependency injection system.

Program.cs

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;

namespace AspNetCoreTasksAndCancellationTokenExample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            // Register BackgroundService as a singleton.
            services.AddSingleton<BackgroundService>();

            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Step 4: Add Controllers to Handle HTTP Requests

Now, we add controllers to start and cancel the task.

Controllers/TaskController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

[ApiController]
[Route("[controller]")]
public class TaskController : ControllerBase
{
    private readonly BackgroundService _backgroundService;
    private readonly ILogger<TaskController> _logger;

    public TaskController(BackgroundService backgroundService, ILogger<TaskController> logger)
    {
        _backgroundService = backgroundService;
        _logger = logger;
    }

    [HttpPost("process")]
    public IActionResult Process()
    {
        _backgroundService.StartProcessing();
        _logger.LogInformation("Received request to start processing.");
        return Ok("Processing started.");
    }

    [HttpPost("cancel")]
    public IActionResult Cancel()
    {
        _backgroundService.CancelProcessing();
        _logger.LogInformation("Received request to cancel processing.");
        return Ok("Processing cancelled.");
    }
}

Step 5: Set Up Logging (Optional)

For better logging in this example, you might want to set up Serilog.

Program.cs Make sure to install the Serilog.AspNetCore package via NuGet:

dotnet add package Serilog.AspNetCore

Then configure Serilog:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;

public class Program
{
    public static void Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .WriteTo.Console()
            .CreateLogger();

        try
        {
            Log.Information("Starting web host");
            CreateHostBuilder(args).Build().Run();
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseSerilog() // Use Serilog
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

Step 6: Test the Endpoints

With the application running, you can test the endpoints:

  1. Start the Process Send a POST request to http://localhost:<port>/task/process using a tool like Postman or curl.

    curl -X POST http://localhost:<port>/task/process
    
  2. Cancel the Process While the process is running, send another POST request to http://localhost:<port>/task/cancel.

    curl -X POST http://localhost:<port>/task/cancel
    

You should see logs in the console indicating that the process started, and then either completed or was cancelled based on when you made the cancel request.

Summary

This example covers the basics of using tasks (Task.Run) and cancellation tokens (CancellationTokenSource) within an ASP.NET Core application to manage long-running processes. The BackgroundService class is responsible for starting and cancelling the task. The TaskController provides HTTP endpoints to control these operations.

Top 10 Interview Questions & Answers on ASP.NET Core Using Tasks and Cancellation Tokens

1. What are Tasks in ASP.NET Core?

Answer:
Tasks represent work that is executing asynchronously in .NET applications, including ASP.NET Core. They are used to handle long-running operations without blocking the main thread, improving performance and responsiveness. A Task is an object representing work that has been queued for execution but hasn't yet completed.

2. How do I create a Task in ASP.NET Core?

Answer:
In ASP.NET Core, you can create tasks by using methods of the Task class or by using async and await keywords. The simplest way to get started is with an async method:

public async Task<string> MyAsyncMethod()
{
    await Task.Delay(1000); // Simulates a delay, often a network operation
    return "Completed!";
}

The Task.Delay method creates a task that completes after a specified amount of time.

3. Why should I use Tasks instead of synchronous code?

Answer:
Using tasks enhances application performance by preventing blocking of threads. Synchronous operations tie up the main thread, which can lead to deadlocks in web applications when I/O-bound operations (like database queries) take time to complete. Tasks allow these operations to be performed asynchronously, thereby making your application more responsive and scalable.

4. How does Cancellation Token work with Tasks?

Answer:
A CancellationToken is a notification to a Task that it should attempt to cancel its operations as soon as possible. This is useful for stopping long-running tasks when necessary, such as when a user aborts an HTTP request.

public async Task DoWorkAsync(CancellationToken cancellationToken)
{
    for (int i = 0; i < 10; i++)
    {
        cancellationToken.ThrowIfCancellationRequested(); // Throws if cancellation token is cancelled
        await Task.Delay(1000); // Delay for a second
    }
}

You can pass a CancellationToken to asynchronous methods.

5. How do I request cancellation of a Task using Cancellation Token?

Answer:
To request the cancellation of a Task, you use the CancellationTokenSource. When you call its Cancel() method, it signals all CancellationTokens derived from it to throw an exception on their next cancellation check.

CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(TimeSpan.FromSeconds(2));

try
{
    await DoWorkAsync(source.Token);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Operation was cancelled!");
}

Here, DoWorkAsync will run for a maximum of 2 seconds before being cancelled.

6. Can I use multiple Cancellation Tokens with a single Task?

Answer:
No, you cannot directly use multiple CancellationToken instances with a single task. However, you can combine tokens using the CancellationTokenSource.CreateLinkedTokenSource method, which creates a new token that triggers cancellation when any of the combined tokens are cancelled.

CancellationTokenSource parentSource = new CancellationTokenSource();
parentSource.CancelAfter(TimeSpan.FromSeconds(5));

CancellationTokenSource childSource = new CancellationTokenSource();
childSource.CancelAfter(TimeSpan.FromSeconds(3));

using (CancellationTokenSource linkedSource = 
    CancellationTokenSource.CreateLinkedTokenSource(parentSource.Token, childSource.Token))
{
    await DoWorkAsync(linkedSource.Token); // Will cancel earlier if either parent or child cancels
}

7. When would it be appropriate to use Cancellation Tokens in ASP.NET Core?

Answer:
Cancellation tokens are appropriate in scenarios where operations might need to be interrupted. Common examples include:

  • User requests that are aborted.
  • Long-running background tasks.
  • API calls where responses may be delayed and the task can be stopped if necessary.

8. Are there any best practices when using Tasks and Cancellation Tokens in ASP.NET Core?

Answer:
Yes, here are some best practices:

  • Ensure proper cancellation checks within your async methods so that they can respond to cancellation requests.
  • Avoid catching OperationCanceledException unless you intend to handle it specifically, as this may suppress the notification intended for cancellation.
  • Use HttpClient with cancellation tokens to avoid hanging requests.
  • For long-running tasks, provide feedback or logs to users/administrators about the status.

9. How do I handle exceptions within a Task in ASP.NET Core?

Answer:
You should handle exceptions within the task code using try-catch blocks. If an exception occurs, it is wrapped inside an AggregateException when using .Result or accessed via the Exception property of the Task.

public async Task TryCatchDemoAsync()
{
    try
    {
        await Task.Run(() => { throw new InvalidOperationException("Boom!"); });
    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine($"Caught exception: {ex.Message}");
    }
}

Alternatively, you can handle exceptions after the task completes:

var task = Task.Run(() => { throw new InvalidOperationException("Boom!"); });

try
{
    await task;
}
catch (InvalidOperationException ex)
{
    Console.WriteLine($"Caught exception: {ex.Message}");
}

Using await directly handles exceptions better as it doesn’t wrap them in AggregateException.

10. Can I cancel a Task that hasn't started?

Answer:
No, CancellationTokenSource.Cancel() doesn't prevent a task from starting. It only affects tasks that already observe the cancellation token. If a task isn't started yet, calling Cancel() beforehand won't stop it; it will still execute unless you have your cancellation checks set appropriately at the task's start.

CancellationTokenSource source = new CancellationTokenSource();
source.Cancel();

var task = Task.Run(() =>
{
    source.Token.ThrowIfCancellationRequested(); // Throws immediately as cancelled
}, source.Token);

// task will throw and hence not run till the lambda is reached

This code ensures that the task throws an exception and stops right away because the token is already cancelled before the task starts.

You May Like This Related .NET Topic

Login to post a comment.