C#中使用 CancellationToken 取消异步任务
.NET 提供了一个类方便用来发出操作取消的信号,这个类就是CancellationToken,它的好处在于它可以在任意数量的线程之间、线程池任务之间、Task之间传递信号,并且所需的代码很简单。通常用于下载超时中断、用户取消任务等情况。
CancellationToken 通常搭配 CancellationTokenSource 使用,后者是前者的一个管理类,使用 CancellationTokenSource 的 Token 属性,可以获取CancellationToken,并控制信号的发送。这两个类都属于命名空间 System.Threading
在异步编程中,只需将 Token 作为一个参数传入异步方法中。在异步方法外便能通过 CancellationTokenSource.Cancel 方法发出取消信号或者 CancelAfter 方法在一段时间后发出取消信号,这会改变 Token 的 isCancellationRequested 属性。在异步方法内,通过这个属性获取取消信号,并作出对应的处理操作。
一、定时取消任务
代码示例:
1 using System; 2 using System.Threading.Tasks; 3 using System.Threading; 4 using System.Net.Http; 5 6 namespace CancellationTokenTest 7 { 8 class Program 9 { 10 static async Task Main(string[] args) 11 { 12 CancellationTokenSource cts = new CancellationTokenSource(); 13 //cts.Cancel() //立即发出取消信号 14 //3秒后发出取消信号,模拟取消行为 15 cts.CancelAfter(3000); 16 Console.WriteLine("下载开始"); 17 await DownloadAsync(cts.Token); 18 Console.ReadKey(); 19 } 20 21 static async Task DownloadAsync(CancellationToken ct) 22 { 23 using (HttpClient client = new HttpClient()) 24 { 25 //模拟一个比较耗时的下载的过程 26 for (int i = 0; i < 1000; i++) 27 { 28 string s = await client.GetStringAsync("https://www.baidu.com"); 29 Console.WriteLine(s); 30 31 //ct.ThrowIfCancellationRequested();//直接抛出异常 32 //判断是否需要取消,并自行处理 33 if (ct.IsCancellationRequested) 34 { 35 Console.WriteLine("下载取消"); 36 break; 37 } 38 } 39 40 } 41 } 42 43 } 44 }
3秒之后,任务将自动取消。
二、手动取消任务
手动调用 cts.Cancel() 来取消任务
1 2 static async Task Main(string[] args) 3 { 4 async Task Execute(CancellationToken token) 5 { 6 await Task.Delay(3000, token); 7 Console.WriteLine("Executed"); 8 } 9 10 CancellationTokenSource cts = new CancellationTokenSource(); 11 12 _ = Execute(cts.Token); 13 14 // 手动取消任务 15 cts.Cancel(); 16 17 Console.ReadKey(); 18 }
三、CancellationToken 注册回调
们可以调用 Register()方法,注册Token取消的回调,参数需要传入 Action 委托。
CancellationTokenSource cts = new CancellationTokenSource(1000); cts.Token.Register(() => Console.WriteLine("任务已取消!")); // 开始异步任务 _ = Execute(cts.Token); Console.ReadKey();
Register() 注册回调后,返回一个 CancellationTokenRegistration 对象,同样的,你可以在回调函数执行前,移除注册回调,就像这样:
cts.Token.Register(() => Console.WriteLine("任务已取消!")).Unregister();