.NET 提供了一個類方便用來發出操作取消的訊號,這個類就是CancellationToken,它的好處在於它可以在任意數量的執行緒之間、執行緒池任務之間、Task之間傳遞訊號,並且所需的程式碼很簡單。通常用於下載超時中斷、使用者取消任務等情況。
CancellationToken 通常搭配 CancellationTokenSource 使用,後者是前者的一個管理類,使用 CancellationTokenSource 的 Token 屬性,可以獲取CancellationToken,並控制訊號的傳送。這兩個類都屬於名稱空間 System.Threading
在非同步程式設計中,只需將 Token 作為一個引數傳入非同步方法中。在非同步方法外便能透過 CancellationTokenSource.Cancel 方法發出取消訊號或者 CancelAfter 方法在一段時間後發出取消訊號,這會改變 Token 的 isCancellationRequested 屬性。在非同步方法內,透過這個屬性獲取取消訊號,並作出對應的處理操作。
例如下面的程式碼:
using System;
using System.Threading.Tasks;
using System.Threading;
using System.Net.Http;
namespace CancellationTokenTest
{
class Program
{
static async Task Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
//cts.Cancel() //立即發出取消訊號
//3秒後發出取消訊號,模擬取消行為
cts.CancelAfter(3000);
Console.WriteLine("下載開始");
await DownloadAsync(cts.Token);
Console.ReadKey();
}
static async Task DownloadAsync(CancellationToken ct)
{
using (HttpClient client = new HttpClient())
{
//模擬一個比較耗時的下載的過程
for (int i = 0; i < 30; i++)
{
string s = await client.GetStringAsync("https://kfm.ink");
Console.WriteLine(s);
//ct.ThrowIfCancellationRequested();//直接丟擲異常
//判斷是否需要取消,並自行處理
if (ct.IsCancellationRequested)
{
Console.WriteLine("下載取消");
break;
}
}
}
}
}
}
這裡除了透過 IsCancellationRequested 屬性判斷是否需要取消外,還可以透過 ThrowIfCancellationRequested 方法在需要取消時立即丟擲異常,該異常是 OperationCanceledException
.NET 很多庫的非同步方法都可以傳入 Token,使用時傳入該引數可以降低程式碼的粒度,例如上面這個例子,至少執行一次 GetStringAsync 才有可能中斷,而這一次執行可能耗費大量的時間,透過使用 GetAsync 方法可以解決這個問題:
//傳入Token
HttpResponseMessage response = await client.GetAsync("https://kfm.ink/", ct);
string s = await response.Content.ReadAsStringAsync();
Console.WriteLine(s);