首页 > 编程语言 >NetCore异步编程CancellationToken

NetCore异步编程CancellationToken

时间:2022-08-24 14:44:36浏览次数:112  
标签:CancellationToken await return 异步 NetCore var async

十年河东,十年河西,莫欺少年穷

学无止境,精益求精

关于异步编程,很久之前就写过一遍博客:asp.net core 系列 5 项目实战之:NetCore 的 async 和 await(参考自:Microsoft教程)

今天继续探讨NetCore异步编程,这里会掺杂其他知识点一块讲解

借助之前博客中的一张图,你看到这张图后是否能给出程序的执行顺序?如果你没能回答正确,建议看上述标出的那篇博客。

上图中,五个任务的执行顺序是什么?

这里不做解答,我们从这张图开启本篇

1、异步编程线程切换

同步执行的方法中,方法被执行后,一直到执行结果,整个过程会由一个进程执行。

但异步编程时,当遇到耗时比较长的异步方法时,会引起线程切换,所谓线程切换是指会开启一个新的线程执行耗时较长的异步方法。

先来一个同步执行,如下:

        static  void Main(string[] args)
        {
            Console.WriteLine("当前线程ID为:" + Thread.CurrentThread.ManagedThreadId);
            DownLoadPage_2();
            Console.WriteLine("当前线程ID为:" + Thread.CurrentThread.ManagedThreadId);
        }

        static void DownLoadPage_2()
        {
            for (int i = 0; i < 10; i++)
            {
                System.Text.StringBuilder sb = new System.Text.StringBuilder("");
                for (int j = 0; j < 100; j++)
                {
                    sb.Append("我是一只小小小鸟,飞也飞不高。");
                    File.WriteAllText(@"E:\Test\task_1.txt", sb.ToString());
                }
            }
        }
View Code

执行结果

 再来一个异步执行的,如下:

        static async Task Main(string[] args)
        {
            Console.WriteLine("当前线程ID为:" + Thread.CurrentThread.ManagedThreadId);
            await DownLoadPage();
            Console.WriteLine("当前线程ID为:" + Thread.CurrentThread.ManagedThreadId);
        }

        static async Task DownLoadPage()
        {
            for (int i = 0; i < 10; i++)
            {
                System.Text.StringBuilder sb = new System.Text.StringBuilder("");
                for (int j = 0; j < 100; j++)
                {
                    sb.Append("我是一只小小小鸟,飞也飞不高。");
                  await  File.WriteAllTextAsync(@"E:\Test\task_1.txt", sb.ToString());
                }
            }
        }
View Code

 综上所示,当执行耗时较长的异步方法后,会出现线程切换的现象,如果异步执行耗时较短,也有可能不切换线程、

因此:

在async 修饰的方法中尽量不要使用   Thread.Sleep(1000); ,应该使用  await Task.Delay(1000); 

Thread.Sleep(1000); 方法阻塞的是所有线程,await Task.Delay(1000); 阻塞的是当前线程。

2、CancellationToken的使用

很多异步方法中,都会有CancellationToken参数,如下:

那么,CancellationToken参数有什么作用呢?

2.1、主动取消任务

创建一个 CancellationTokenSource,然后调用异步方法时,传入 CancellationToken,它是一个轻量级对象,可以通知请求是否已取消,我们可以手动调用 cts.Cancel() 来取消任务,为了方面演示,这里我有用到局部方法。

下面的方法,当遇到输入 q 时,方法停止执行

        static async Task Main(string[] args)
        {
            CancellationTokenSource source = new CancellationTokenSource();
            source.CancelAfter(4 * 1000);//运行时间超过4秒,则取消执行
          
            try
            {
                await DownLoadPage_3("http://www.baidu.com", 200, source.Token);
                //输入q 请求被取消
                while (Console.ReadLine() == "q")
                {
                    source.Cancel();
                }
            }
            catch
            { 
                Console.WriteLine($"下载超时被取消了");
            }
        }
        //简单的下载任务
        public static async Task DownLoadPage_3(string uri, int num, CancellationToken token)
        {
            using (HttpClient client = new HttpClient())
            {
                for (int i = 0; i < num; i++)
                {
                    var html = await client.GetAsync(uri, token);
                    Console.WriteLine($"第{i + 1}次下载");
                    //抛出被取消的异常
                    token.ThrowIfCancellationRequested();
                }
            }
        }
View Code

2.2、定时取消任务

在生产的 CancellationToken中指定请求过期时间,下面的例子为4秒钟过期,4秒钟后,请求终止、

        static async Task Main(string[] args)
        {
            CancellationTokenSource source = new CancellationTokenSource();
            source.CancelAfter(4 * 1000);//运行时间超过4秒,则取消执行
           

            try
            {
                await DownLoadPage_3("http://www.baidu.com", 200, source.Token);
            }
            catch
            {
                File.WriteAllText(@"E:\Test\hello.txt", "下载超时被取消了");
                Console.WriteLine($"下载超时被取消了");
            } 
        }
        //简单的下载任务
        public static async Task DownLoadPage_3(string uri, int num, CancellationToken token)
        {
            using (HttpClient client = new HttpClient())
            {
                for (int i = 0; i < num; i++)
                {
                    var html = await client.GetAsync(uri, token);
                    Console.WriteLine($"第{i + 1}次下载");
                    //抛出被取消的异常
                    token.ThrowIfCancellationRequested();
                }
            }
        }
View Code

2.3、HttpClient下载过期时间设定

上述2.2的例子设置为4秒钟后,下载任务终止,因此,我们在使用HttpClinet进行下载操作时,可以设定过期时间,这样做的好处是防止因网络等其他问题导致程序一致处于执行下载过程中,

2.4、在 WebAPI中使用

在webApi中,建议将各个控制器的Action中都注入CancellationToken参数

这样做的好处时,当用户终止操作时,即使程序还在执行,也会随着用户的终止而终止

        [HttpGet]
        public async Task<IActionResult> Get(CancellationToken token = default)
        {
           var s = await DownLoadPage(token);
            return Ok(s);
        }

        async Task<string> DownLoadPage(CancellationToken token = default)
        {
            var result = string.Empty;
            using (HttpClient client = new HttpClient())
            {
                for (int i = 0; i < 100; i++)
                {
                    var s = await client.GetStringAsync("http://www.baidu.com", token);
                    result += s;
                    System.IO.File.WriteAllText(@"E:\Test\hello.txt", $"第{i + 1}次写入");

                }
                return result;
            }
        }

上边的例子是一个简单的下载和写入任务,没有实际的意义,下载是为了耗时,写入是为了方便演示效果。

 执行结果:

 说明随着用户终止操作,方法停止执行。

现在将方法改成如下:

        [HttpGet]
        public async Task<IActionResult> Get(CancellationToken token = default)
        {
           var s = await DownLoadPage(CancellationToken.None);
            return Ok(s);
        }

 

 不传入CancellationToken,按照上述操作执行方法2秒左右后,将网址定向为百度.com,10秒钟后,看下文本文件写入的次数。

 不传入CancellationToken,方法不会随着用户的终止停止执行。

因此:

在webApi中,建议将各个控制器的Action中都注入CancellationToken参数

3、WhenAll  、WhenAny、yield return 的使用

WhenAll 是指所有异步方法都执行完毕后在执行其他

whenAny是指异步方法中有任何一个执行完成后执行其他

yield return 流水化操作,用于提升性能,可用于多任务下载场景使用

本篇博客不做讲解,只贴出代码,如下:

    internal class Program
    {
        static async Task Main(string[] args)
        {
            var task_1 = System.IO.File.ReadAllTextAsync(@"E:\Test\task_1.txt");
            var task_2 = System.IO.File.ReadAllTextAsync(@"E:\Test\task_2.txt");
            var task_3 = System.IO.File.ReadAllTextAsync(@"E:\Test\task_3.txt");
            var result =await Task.WhenAll<string>(task_1, task_2, task_3);
            foreach(var item in result)
            {
                Console.WriteLine(item);
            }
            //流水化操作  提升性能  async 中不能使用 yield
            // yield return 是取到一条数据就返回,适合用于下载场景,例如 yield return "张" 后,会进入Foreach循环
            foreach (var item in GetStringArty())
            {
                Console.WriteLine(item);
            }
            await foreach (var item in GetStringArty2())
            {
                Console.WriteLine(item);
            }
            //List<string> 是填充满后,才会进入foreach  
            foreach (var item in GetStringArty_2())
            {
                Console.WriteLine(item);
            }
        }

        public static IEnumerable<string> GetStringArty()
        {
            yield return "张";
            yield return "李";
            yield return "陈";
        }
        public static async IAsyncEnumerable<string> GetStringArty2()
        {
            yield return  "张";
            yield return "李";
            yield return "陈";
        }

        public static IEnumerable<string> GetStringArty_2()
        {
            List<string> arys = new List<string>();
            arys.Add("张");
            arys.Add("李");
            arys.Add("陈");
            return arys; 
        }
    }

@天才卧龙的博客

 

标签:CancellationToken,await,return,异步,NetCore,var,async
From: https://www.cnblogs.com/chenwolong/p/CancellationToken.html

相关文章

  • 综合案例-黑马旅游网_异步提交表单和servlet代码实现
    综合案例-黑马旅游网_异步提交表单在此使用异步提交表单是为了获取服务器响应的数据因为前台使用的是html作为视图层不能够直接从servlet相关的域对象获取值只能通过aj......
  • PHP+AJAX实现异步上传文件
    文件上传功能是动态Web应用程序的常用功能。通常,php采用提交表单并刷新页面的方法上传文件。但是,如果您想提供更好的用户体验,则可以使用jQuery和Ajax来上传文件而无需刷新......
  • 同步、异步、阻塞和非阻塞
    最开始看到这四个名词的时候,感觉很困惑,这不都一样吗?后来就查了查。最近又想起来这几个概念,发现记忆有些模糊,故写此博客。1.同步调用者调用被调用者,当调用者没有获取到......
  • 异步提交表单以及代码实现
    异步提交表单在此使用异步提交表单是为了获取服务器响应的数据。因为我们前台使用的是html作为视图层,不能够直接从servlet相关的域对象获取值,只能通过ajax获取响应数据......
  • 函数式编程与 JS 异步编程、手写 Promise
    目录函数式编程与JS异步编程、手写Promise简答题函数式编程与JS异步编程、手写Promise简答题谈谈你是如何理解JS异步编程的?EventLoop、消息队列都是做什么的?......
  • C#任务取消--CancellationTokenSource取消令牌源
    一、概述在讲任务取消之前,得先了解取消令牌跟取消令牌源,实现任务取消功能的就是依靠这两个。CancellationTokenSource:取消令牌源类,拥有Cancel()方法,可以给关联的令牌......
  • 乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 官方扩展集锦(Microsoft.AspN
    什么是Microsoft.AspNetCorehttps://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/metapackage-appASP.NETCore共享框架(Microsoft.AspNetCore.App)包含由Micr......
  • 阻塞非阻塞与同步异步的区别
    阻塞非阻塞与同步异步的区别同步/异步关注的是消息通知的机制,而阻塞/非阻塞关注的是程序(线程)等待消息通知时的状态。以小明下载文件打个比方,同步阻塞:小明一直盯着下载......
  • springboot使用@Async实现异步调用
    异步与同步同步就是整个处理过程顺序执行,当各个过程都执行完毕,并返回结果。异步调用则是只是发送了调用的指令,调用者无需等待被调用的方法完全执行完毕;而是继续执行......
  • 10.异步mysql
    python中操作mysql连接、操作、断开都是网络IO#安装支持异步aiomysql的模块pip3installaiomysqlasyncdefexecute():#网络IO操作,连接数据库,遇到IO切换任务......