取消任务
分类: .Net技术 ◆ 标签: #异步编程 #基础 #.Net ◆ 发布于: 2023-06-04 19:09:31

我们前面几章学习了异步编程的基本用法和基本理论,本章来学习如何取消一个Task
。
用户可以取消还没有完成的任务,实际的基本原理也很简单,只需要给没个异步方法带入CancellationTokenSource
的实例就可以了,下面的例子演示如何取消一个任务,您可以从这里找到示例代码:https://github.com/hylinux/azure-demo/tree/main/dotnet/basic/asyncdemo/CancelDemo
该实例的场景是取消从网络上下载内容的web客户端。
创建控制台应用
使用如下的命令行创建控制台应用
mkdir CancelDemo
cd CancelDemo
dotnet new console
替换using
语句
使用编辑器,例如VS code
,打开Program.cs
文件,将using statement
替换为如下的内容
using System; using System.Collections.Generic; using System.Diagnostics; using System.Net.Http; using System.Threading; using System.Threading.Tasks;
添加字段
在类Program
中添加如下字段的定义:
static readonly CancellationTokenSource s_cts = new CancellationTokenSource(); static readonly HttpClient s_client = new HttpClient { MaxResponseContentBufferSize = 1_000_000 }; static readonly IEnumerable<string> s_urlList = new string[] { "https://docs.microsoft.com", "https://docs.microsoft.com/aspnet/core", "https://docs.microsoft.com/azure", "https://docs.microsoft.com/azure/devops", "https://docs.microsoft.com/dotnet", "https://docs.microsoft.com/dynamics365", "https://docs.microsoft.com/education", "https://docs.microsoft.com/enterprise-mobility-security", "https://docs.microsoft.com/gaming", "https://docs.microsoft.com/graph", "https://docs.microsoft.com/microsoft-365", "https://docs.microsoft.com/office", "https://docs.microsoft.com/powershell", "https://docs.microsoft.com/sql", "https://docs.microsoft.com/surface", "https://docs.microsoft.com/system-center", "https://docs.microsoft.com/visualstudio", "https://docs.microsoft.com/windows", "https://docs.microsoft.com/xamarin" };
这里CancellationTokenSource
用作一个信号源,它会发送一个CancellationToken
给到需要取消的方法。
更新Main
方法
更新一下Main
方法如下:
static async Task Main() { Console.WriteLine("Application started."); Console.WriteLine("Press the ENTER key to cancel...\n"); Task cancelTask = Task.Run(() => { while (Console.ReadKey().Key != ConsoleKey.Enter) { Console.WriteLine("Press the ENTER key to cancel..."); } Console.WriteLine("\nENTER key pressed: cancelling downloads.\n"); s_cts.Cancel(); }); Task sumPageSizesTask = SumPageSizesAsync(); await Task.WhenAny(new[] { cancelTask, sumPageSizesTask }); Console.WriteLine("Application ending."); }
注意到我们在主线程里创建了一个CPU-bound
的异步方法(直接使用Task.run()
来运行的异步方法), 然后再调用另外一个异步方法SumPageSizesAsync()
, 最后使用Task.WhenAny()
来判断列表中的任务完成。
创建异步方法SumPageSizesAsync()
这里我们创建我们需要的异步方法
static async Task SumPageSizesAsync() { var stopwatch = Stopwatch.StartNew(); int total = 0; foreach (string url in s_urlList) { int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token); total += contentLength; } stopwatch.Stop(); Console.WriteLine($"\nTotal bytes returned: {total:#,#}"); Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n"); }
需要注意的是异步方法ProcessUrlAsync()
的参数里有一个s_cts
的变量,该变量即为CancellationTokenSource
的实例。
这里看到的是如何定义一个可取消的调用,我们再来看如何定义这样的异步方法
定义可取消的异步方法
我们这里来定义可取消的异步方法:
static async Task<int> ProcessUrlAsync(string url, HttpClient client, CancellationToken token) { HttpResponseMessage response = await client.GetAsync(url, token); byte[] content = await response.Content.ReadAsByteArrayAsync(token); Console.WriteLine($"{url,-60} {content.Length,10:#,#}"); return content.Length; }
好了,到这里整个实例算是定义完成了,可以看一下取消的效果。