CancellationToken结构体:
有时需要提前终止任务,比如:请求超时、用户取消请求
很多异步方法都有CancellationToken参数,用于获得提前终止的信号
- None:空
- bool IsCancellationRequested:是否取消
- Register(Action callback):注册取消监听
- ThrowlfCancellationRequested():如果任务被取消,执行到这句话就抛异常
CancellationTokenSource:
- CancelAfter():超时后发出取消信号
- Cancel():发出取消信号
- CancellationToken Token:用于创建CancellationToken结构体
//用户敲击按键取消异步操作
CancellationTokenSource tokens = new CancellationTokenSource();
CancellationToken token = tokens.Token;
DownloadlAsync3("https://www.baidu.com", 100, token);
while (Console.ReadLine() != "q")
{
}
tokens.Cancel();
Console.ReadLine();
//超时后取消异步操作
static async Task Main1(string[] args)
{
CancellationTokenSource tokens = new CancellationTokenSource();
//3秒后超时
tokens.CancelAfter(3000);
CancellationToken token = tokens.Token;
await DownloadlAsync3("https://www.baidu.com", 100, token);
}
//不取消异步操作代码
static async Task DownloadlAsync(string url, int n)
{
using (HttpClient httpClient = new HttpClient())
{
for (int i = 0; i < n; i++)
{
string html = await httpClient.GetStringAsync(url);
Console.WriteLine($"{DateTime.Now}:{html}");
}
}
}
//两种手动取消异步操作代码的方式
static async Task DownloadlAsync2(string url, int n, CancellationToken token)
{
using (HttpClient httpClient = new HttpClient())
{
for (int i = 0; i < n; i++)
{
string html = await httpClient.GetStringAsync(url);
Console.WriteLine($"{DateTime.Now}:{html}");
//方式一:通过IsCancellationRequested判断是否超时,取消异步操作
/*
if (token.IsCancellationRequested)
{
Console.WriteLine("请求被取消");
break;
}
*/
//方式二:超时抛出异常,取消异步操作
token.ThrowIfCancellationRequested();
}
}
}
//GetAsync自动取消异步操作
static async Task DownloadlAsync3(string url, int n, CancellationToken token)
{
using (HttpClient httpClient = new HttpClient())
{
for (int i = 0; i < n; i++)
{
//返回Task<HttpResponseMessage>
var resp = await httpClient.GetAsync(url, token);
//将HTTP内容序列化成字符串,异步操作
string html = await resp.Content.ReadAsStringAsync();
Console.WriteLine($"{DateTime.Now}:{html}");
}
}
}
ASP.NET Core开发中,一般不需要自己处理CancellationToken、CancellationTokenSource,只要做到“能转发CancellationToken就转发”即可。ASP.NET Core会对于用户请求中断进行处理。
Web开发中CancellationToken的使用:
Web开发中,网页处理时间较长时,用户关闭浏览器或访问其他网页时,如果该网页的请求在服务器端没有完成,则服务器会自动终止访问请求,以此来节省服务器资源
//在.NET Core MVC中,控制器增加一个CancellationToken参数,该参数由.NET框架进行赋值
//当网页关闭或刷新时,.NET框架会自动传递信号取消异步操作
public async Task<IActionResult> Index(CancellationToken cancellationToken)
{
await DownloadlAsync("https://www.baidu.com", 10, cancellationToken);
return View();
}
static async Task DownloadlAsync(string url, int n, CancellationToken cancellationToken)
{
using (HttpClient httpClient = new HttpClient())
{
for (int i = 0; i < n; i++)
{
var resp = await httpClient.GetAsync(url, cancellationToken);
string html = await resp.Content.ReadAsStringAsync();
Debug.WriteLine(html);
}
}
}
Task类中 WhenAll与WhenAny的使用
Task.WhenAny(Task[]):任何一个Task完成,Task就完成
Task.WhenAll(Task[]):所有Task完成,Task才完成。用于多个任务执行结束,但是不在乎它们的执行顺序
FromResult():创建普通数值的Task对象
static async Task Main(string[] args)
{
//异步方式读出文件信息,
Task<string> t1 = File.ReadAllTextAsync(@"E:\temp\a.txt");
Task<string> t2 = File.ReadAllTextAsync(@"E:\temp\b.txt");
Task<string> t3 = File.ReadAllTextAsync(@"E:\temp\c.txt");
//等待三个任务结束
string[] str = await Task.WhenAll(t1, t2, t3);
Console.WriteLine(str[0]);
Console.WriteLine(str[1]);
Console.WriteLine(str[2]);
}
练习: 计算应该文件夹下,所有文本文件的单词个数汇总
static async Task Main(string[] args)
{
//获取文件夹下所有文件路径
string[] files = Directory.GetFiles(@"E:\temp");
//创建数组存储每个文件字符数量
Task<int>[] countTasks = new Task<int>[files.Length];
for (int i = 0; i < files.Length; i++)
{
string filename = files[i];
Task<int> t = ReadCharsCount(filename);
countTasks[i] = t;
}
int[] counts = await Task.WhenAll(countTasks);
int c = counts.Sum();
Console.WriteLine(c);
}
/// <summary>
/// 文件共有多少个字符
/// </summary>
/// <param name="filname">文件路径</param>
/// <returns>字符数量</returns>
static async Task<int> ReadCharsCount(string filname)
{
string s = await File.ReadAllTextAsync(filname);
return s.Length;
}