Tối Ưu Hóa Tác Vụ Bất Đồng Bộ Với CancellationToken Trong .NET Core

Trong quá trình phát triển ứng dụng .NET Core, việc quản lý vòng đời của các tác vụ bất đồng bộ (async tasks) đóng vai trò then chốt để đảm bảo hiệu suất hệ thống. CancellationToken là cơ chế cho phép hợp tác hủy bỏ các thao tác đang chạy. Nếu không sử dụng đúng cách, các tác vụ nền có thể tiếp tục tiêu tốn tài nguyên ngay cả khi yêu cầu ban đầu đã bị chấm dứt hoặc xảy ra lỗi.

Một vấn đề phổ biến là khi khởi tạo Task để xử lý logic nặng hoặc gọi dịch vụ bên ngoài mà không truyền kèm token hủy, các tiến trình này sẽ chạy độc lập. Khi luồng chính gặp sự cố hoặc bị终止, các tác vụ con vẫn hoạt động, dẫn đến lãng phí bộ nhớ và CPU. Dưới đây là các phương án triển khai CancellationToken trong những tình huống thực tế.

Quản lý thời gian chờ với HttpClient

HttpClient thường được dùng để gọi API bên ngoài. Ngoài việc thiết lập thuộc tính Timeout, chúng ta có thể kiểm soát thời gian chờ linh hoạt hơn thông qua CancellationTokenSource. Lợi ích chính là khả năng truyền trạng thái hủy dọc theo chuỗi gọi hàm.

public async Task<string> FetchExternalDataAsync(CancellationToken cancellationToken = default)
{
    var httpClient = _httpClientFactory.CreateClient();
    var response = await httpClient.GetAsync("https://api.example.com/data", cancellationToken);
    
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync(cancellationToken);
}

Tại nơi gọi hàm, chúng ta thiết lập thời gian chờ cụ thể:

public async Task<string> ProcessRequest()
{
    using var timeoutSource = new CancellationTokenSource(TimeSpan.FromSeconds(2));
    try 
    {
        var data = await _externalService.FetchExternalDataAsync(timeoutSource.Token);
        return data;
    }
    catch (OperationCanceledException)
    {
        // Xử lý khi yêu cầu bị hủy do超时
        return null;
    }
}

Tích hợp hủy bỏ trong gRPC

Khi xây dựng dịch vụ gRPC, ngữ gọi máy chủ (ServerCallContext) đã cung cấp sẵn một CancellationToken. Token này đồng bộ với trạng thái kết nối từ client. Nếu client ngắt kết nối, server sẽ nhận được tín hiệu hủy thông qua token này.

Triển khai phía Server:

public override async Task<UserResponse> GetUsers(EmptyRequest request, ServerCallContext context)
{
    // Giả lập độ trễ xử lý
    await Task.Delay(500, context.CancellationToken);
    
    var dataList = await _database.GetUsersAsync(context.CancellationToken);
    var response = new UserResponse();
    
    foreach (var user in dataList)
    {
        response.Items.Add(new UserDto { Id = user.Id, FullName = user.Name });
    }
    
    return response;
}

Phía Client, khi gọi dịch vụ, chúng ta có thể khởi tạo CancellationTokenSource để giới hạn thời gian chờ cho toàn bộ lời gọi remote:

public class GrpcClientWrapper
{
    private readonly UserClient _client;

    public GrpcClientWrapper(string address)
    {
        var channel = GrpcChannel.ForAddress(address);
        _client = new UserClient(channel);
    }

    public async Task<UserResponse> GetUsersAsync(CancellationToken token = default)
    {
        return await _client.GetUsersAsync(new EmptyRequest(), cancellationToken: token);
    }
}

Tại Controller hoặc lớp điều phối:

[HttpGet("users")]
public async Task<IActionResult> ListUsers()
{
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
    var result = await _grpcClient.GetUsersAsync(cts.Token);
    return Ok(result);
}

Xử lý hủy yêu cầu trong WebAPI

Trong các ứng dụng web, người dùng có thể đóng tab,刷新 trang hoặc hủy request khi đang tải. Phía máy chủ nếu không nhận biết được điều này sẽ tiếp tục thực thi logic, gây tốn tài nguyên vô ích. ASP.NET Core tự động liên kết HttpContext.RequestAborted với tham số CancellationToken trong Action.

Khi khai báo tham số CancellationToken trong method của Controller, framework sẽ truyền token hủy vào đó. Nếu client ngắt kết nối, token này sẽ chuyển sang trạng thái hủy bỏ.

[HttpGet("process")]
public async Task<string> ExecuteLongTask(CancellationToken cancellationToken)
{
    try
    {
        await _businessService.RunHeavyLogic(cancellationToken);
        
        // Điểm kiểm tra hủy bỏ
        await Task.Delay(10000, cancellationToken);
        
        await _externalApi.NotifyCompletion(cancellationToken);
    }
    catch (OperationCanceledException)
    {
        // Ghi log hoặc dọn dẹp tài nguyên khi request bị hủy
        _logger.LogWarning("Request was cancelled by client");
        throw; 
    }

    return "Completed";
}

Việc truyền token xuống các lớp dịch vụ bên dưới (repository, external API) là bắt buộc để đảm bảo toàn bộ chuỗi xử lý dừng lại ngay lập tức khi có tín hiệu hủy. Đối với các exception phát sinh do hủy bỏ, nên sử dụng Action Filter hoặc Middleware để xử lý tập trung, tránh làm rối logic nghiệp vụ chính.

Thẻ: dotnet-core cancellation-token async-await grpc webapi

Đăng vào ngày 18 tháng 6 lúc 03:42