Giả sử ta có 1 API như sau:
[HttpGet("/{times}")] public decimal Get(int times, CancellationToken cancellationToken) { decimal result = 0; for (int i = 0; i < times; i++) { Thread.Sleep(1000); result += i; } return result; }
Mở trình duyệt lên và gõ http://<baseURL>/5, ta nhận được kết quả tổng các số từ 0 đến 4 (tức 5-1).
Ta có thể thêm dòng cancellationToken.ThrowIfCancellationRequested() vào trong vòng for để dừng thực thi khi có tín hiệu huỷ bỏ (đóng trình duyệt chẳng hạn).
Tuy nhiên, không phải API nào cũng có vòng for để ta làm như vậy. Nhiều khi ta cần thực hiện tuần tự một/một loạt dòng lệnh, ví như lấy dữ liệu chỗ A, ghép dữ liệu chỗ B,… Ta cần một cơ chế nào đó mà đang lấy dữ liệu chưa xong, thấy tín hiệu huỷ là phải huỷ luôn, chứ lại đợi nó xong mới huỷ thì chả giảm tải được bao nhiêu.
Thằng CancellationToken có một phương thức rất hay, đó là Register, truyền vào 1 Action. Action đó sẽ được thực thi khi có tín hiệu cancel. Cách sử dụng như sau:
TaskCompletionSource<decimal> taskCompletionSource = new(); cancellationToken.Register(() =>; { taskCompletionSource.SetCanceled(); });
Khi SetCanceled được gọi, taskCompletionSource sẽ được Complete.
Thằng Task có phương thức WhenAny, cho phép ra tín hiệu Complete khi một trong số task trong danh sách truyền vào Complete. Lợi dụng điều đó, ta truyền vào taskCompletionSource và 1 task nữa chính là đoạn code chính của API ban đầu. Kết quả ta có được như sau:
[HttpGet("{times}")] public async Task<decimal> Get(int times, CancellationToken cancellationToken) { TaskCompletionSource<decimal> taskCompletionSource = new(); cancellationToken.Register(() =>; { taskCompletionSource.SetCanceled(); }); Task<decimal> task = Task.Run(() =>; { decimal result = 0; for (int i = 0; i < times; i++) { Thread.Sleep(1000); result += i; } return result; }); Task<decimal> completedTask = await Task.WhenAny(task, taskCompletionSource.Task); var result = await completedTask; Console.Writeln("Finished"); return result ; }
Quay lại trình duyệt, chờ đủ 5 giây sau khi bấm F5, ta sẽ nhận được kết quả, cùng với dòng Finished được in ra cửa sổ Console phía ứng dụng .Net Core API.
Thử nghiệm F5 lần nữa rồi đóng trình duyệt ngay sau 1-2 giây, ta thấy phía Console ném ra một Exception, và không có dòng Finished nào được in ra cả. Vậy là đã hoàn thành.