c#之CancellationToken的使用

在.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();

c#之CancellationToken的使用_第1张图片

二、上面介绍完基本使用,下面使用下具体场景

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键时就可以取消任务了。

c#之CancellationToken的使用_第2张图片

2.2 web api程序中取消操作只需要在接口参数加上CancellationToken就可以了,当我们取消请求的时候,会自动将CancellationToken传进来

[HttpGet]
        public async Task 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("下载任务取消!");
            }
        }

方便演示,直接用浏览器访问接口,然后马上取消

c#之CancellationToken的使用_第3张图片

 

c#之CancellationToken的使用_第4张图片

 

此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,此时也会同时取消新的关联任务

c#之CancellationToken的使用_第5张图片

 

你可能感兴趣的:(c#)