在.net core随处可见的async await的异步使用,这就可以用到CancellationToken来取消任务。
场景一:如在web应用中,用户点击了下载文件(耗时操作),当用户不想下载了,直接点取消,但是我们异步代码中没响应取消的话,还是会执行下去这个耗时操作的,这就耗费了不必要的服务器资源。
一、开始使用
CancellationTokenSource:用于控制和生成CancellationToken
CancellationToken:取消令牌,可以注册取消回调等操作
1、先实例化CancellationTokenSource
2、注册取消回调
3、启动一个Task,模拟耗时操作
4.调用CancellationTokenSource的CancelAfter方法,表示多少毫秒后取消,也可使用Cancel方法立即取消
5、IsCancellationRequested属性判断任务是否取消,没取消则一直输出i
CancellationTokenSource cts=new CancellationTokenSource();
cts.Token.Register(() => { Console.WriteLine("任务已停止"); });
Task.Run(() =>
{
int i = 1;
while (!cts.IsCancellationRequested)
{
i++;
Console.WriteLine(i);
Task.Delay(500).Wait();
}
});
cts.CancelAfter(2000);
Console.ReadLine();
二、上面介绍完基本使用,下面使用下具体场景
2.1 如桌面应用,当用户按下某个键的时候取消我当前的任务
简单改造下上面的代码就好了
CancellationTokenSource cts=new CancellationTokenSource();
cts.Token.Register(() => { Console.WriteLine("任务已停止"); });
Task.Run(() =>
{
int i = 1;
while (!cts.IsCancellationRequested)
{
i++;
Console.WriteLine(i);
Task.Delay(500).Wait();
}
});
var key=Console.ReadKey();
if(key.Key==ConsoleKey.A)
{
cts.Cancel();
}
Console.ReadLine();
当我们按下a键时就可以取消任务了。
2.2 web api程序中取消操作只需要在接口参数加上CancellationToken就可以了,当我们取消请求的时候,会自动将CancellationToken传进来
[HttpGet]
public async Task<IActionResult> CancelDownLoad(CancellationToken cancellationToken)
{
try
{
var _client = _httpClient.CreateClient("bigDownLoad");
_client.DefaultRequestHeaders.Range = new RangeHeaderValue(0, 1024 * 1024 * 50);
await Task.Delay(1000);
if (!cancellationToken.IsCancellationRequested)
{
var resp = await _client.GetAsync("http://du.cainiaoxt.cn/dd.php/windows_7_ultimate_x64_2023.iso",cancellationToken);
if (resp.StatusCode != System.Net.HttpStatusCode.OK)
{
string chunkFileFolder = @"D:\";
string bigFileName = chunkFileFolder + @"\bigFile" + new Random().Next(0, 10);
using (FileStream fs = new FileStream(bigFileName, FileMode.Create))
{
var respStream = await resp.Content.ReadAsStreamAsync();
await respStream.CopyToAsync(fs);
await respStream.FlushAsync();
}
}
return Ok();
}
else
{
cancellationToken.ThrowIfCancellationRequested();
return BadRequest("下载任务取消!");
}
}
catch (OperationCanceledException ex)
{
Console.WriteLine("下载任务取消!"+ex.Message);
return BadRequest("下载任务取消!");
}
}
方便演示,直接用浏览器访问接口,然后马上取消
此CancellationToken参数也可以传递给其他异步方法,IsCancellationRequested属性就是判断任务是否取消,如果取消我们可以调用ThrowIfCancellationRequested方法来抛出OperationCanceledException异常,进行其他操作。
三、关联取消
关联取消就是将多个CancellationToken关联起来生成一个新的CancellationTokenSource,当关联的CancellationTokenSource有一个取消时,这个新的CancellationTokenSource也会被取消
// See https://aka.ms/new-console-template for more information
CancellationTokenSource cts=new CancellationTokenSource();
cts.Token.Register(() => { Console.WriteLine("任务1已停止"); });
CancellationTokenSource cts2 = new CancellationTokenSource();
cts2.Token.Register(() => { Console.WriteLine("任务2已停止"); });
CancellationTokenSource cts3 = new CancellationTokenSource();
cts3.Token.Register(() => { Console.WriteLine("任务3已停止"); });
var linkTokenSource=CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cts2.Token, cts3.Token);
linkTokenSource.Token.Register(() => { Console.WriteLine("关联token取消"); });
Task.Run(() =>
{
int i = 1;
while (!cts.IsCancellationRequested)
{
i++;
Console.WriteLine("任务1:"+i);
Task.Delay(500).Wait();
}
});
Task.Run(() =>
{
int i = 1;
while (!cts2.IsCancellationRequested)
{
i++;
Console.WriteLine("任务2:"+i);
Task.Delay(500).Wait();
}
});
Task.Run(() =>
{
int i = 1;
while (!cts3.IsCancellationRequested)
{
i++;
Console.WriteLine("任务3:"+i);
Task.Delay(500).Wait();
}
});
Task.Run(() =>
{
int i = 1;
while (!linkTokenSource.IsCancellationRequested)
{
i++;
Console.WriteLine("关联token任务:" + i);
Task.Delay(500).Wait();
}
});
var key=Console.ReadKey();
if(key.Key==ConsoleKey.A)
{
cts.Cancel();
}
Console.ReadLine();
改造一下上面的代码,使用CancellationTokenSource的静态方法CreateLinkedTokenSource将多个CancellationTokenSource关联起来,当我们输入a时,取消任务1,此时也会同时取消新的关联任务