专栏首页DotNet程序园Asp.Net Core 轻松学-多线程之Task(补充)

Asp.Net Core 轻松学-多线程之Task(补充)

前言

    在上一章 Asp.Net Core 轻松学-多线程之Task快速上手 文章中,介绍了使用Task的各种常用场景,但是感觉有部分内容还没有完善,在这里补充一下。

1. 任务的等待

在使用 Task 进行基于队列的异步任务(TAP)的时候,对于刚入门的同学来说,只是简单的了解了使用 Task 可以在后台处理异步任务,但是对于阻塞调用可能还有有一些不太明白,异步任务默认是不阻塞的执行过程,当一个 Task 被创建出来的时候,并没有被压入队列中,而是开始执行的时候,才会进入队列中;执行一个异步任务可以设置等待

1.1 使用 await 进行等待任务完成
[HttpGet]
     public async Task<string> Get()
        {
            string result = string.Empty;
            await Task.Run(() =>
             {
                 result = "Hello World!";
             });
            return result;
        }

上面的异步方法 Get() 内部执行了一个 Task,为了保持方法在返回的时候能够给变量 result 设置值,这里使用了 await 等待任务完成,在任务未完成前,即 result 未被设置值 Hello World! 前,该接口将一直阻塞,直到任务完成。

1.1 使用 Wait()
[HttpGet("WaitTest")]
        public string WaitTest(int id)
        {
            string result = string.Empty;
            var waitTask = Task.Run(() =>
             {
                 result = "Hello World!";
             });
            waitTask.Wait(TimeSpan.FromSeconds(5));

            return result;
        }

上面的代码定义了一个 TAP ,waitTask 使用了 Wait() 方法进行等待并传入一个时间,表示等待 5 秒中后退出阻塞过程,

1.2 使用 CancellationToken 方法
[HttpGet("WaitToken")]
        public string WaitToken(int id)
        {
            var result = string.Empty;
            CancellationTokenSource cts = new CancellationTokenSource();
            var taskToken = Task.Run(() =>
            {
                cts.CancelAfter(TimeSpan.FromSeconds(1));
                Task.Delay(2000).Wait();
                result = "Hello World!";
            });
            taskToken.Wait(cts.Token);

            return result;
        }

上面的任务 taskToken ,则使用了取消令牌,当令牌没有收到取消通知的时候,该任务将一直等待, taskToken 任务内部指示取消令牌 1 秒后取消,同时,任务内部使用 Task.Delay 阻塞 2 秒,这很特别,这种设置使得 taskToken 任务将引发任务取消的异常而导致无法给 result 变量进行值设置,如果你对取消令牌不太了解,建议阅读我之前的文章 Asp.Net Core 轻松学-多线程之取消令牌

2. 同步方法中的异步任务

在同步方法中,我们可以非常容易的创建一个 Task 任务,特别是 .Net Core 提供了 Task 这么方便的使用方式的情况下,在某些场景下,就会出现一些意想不到的问题,我的忠告是:在使用 TAP 的时候,尽可能的使用可等待的任务,特别是需要注意不要在 TAP 中运行一些耗时较长的任务,比如批量处理数据、事务过程等等,如果真的是需要有这一类型的任务需要使用 Task 进行处理,那么我的建议是,将这些任务缩小,然后尽快的结束,比如,你可以使用消息队列来执行批量处理数据,而 Task 的任务则缩小至把处理条件丢到消息队列中,这样的解耦才是正确的选择,长时间运行于后台的 TAP,常常导致许多问题,比如消息冒泡、服务重启,都有可能会中断 TAP 线程,要知道,所有的 TAP 都是后台线程,当主进程退出的时候,后台线程也将被清理

3. 手动排队任务

在 TheadPool 内部,提供了一个排队的方法,当线程池资源可用后,将会自动的执行该队列,这样做的好处显而易见,就是你可以通过定义一系列的任务,然后等待线程池去按顺序处理它,这个排队的过程本质上就是队列

3.1 手动排队任务
[HttpGet("TaskQueue")]
        public bool TaskQueue()
        {
            var inQueues = ThreadPool.QueueUserWorkItem(ThreadProc);

            return inQueues;
        }

        private void ThreadProc(Object stateInfo)
        {
            Console.WriteLine("此任务来自线程池队列执行");
        }

上面的代码中,在 TaskQueue() 内部使用 ThreadPool.QueueUserWorkItem() 将方法 ThreadProc(Object stateInfo) 压入队列中,并返回一个值:inQueues ,该值指示任务压入队列是否成功,在 .Net Core 中,ThreadPool.QueueUserWorkItem() 提供了 3 个方法重载,可以按需使用;使用重载方法,甚至可以在压入任务的时候传入参数调用,类似下面的代码

3.2 在排队任务时传递参数
[HttpGet("TaskQueue")]
        public bool TaskQueue()
        {
            var inQueues = ThreadPool.QueueUserWorkItem(ThreadProc);

            var inQueuesSecond = ThreadPool.QueueUserWorkItem(ThreadProc, "这是一条测试消息");

            return inQueues;
        }

        private void ThreadProc(Object stateInfo)
        {
            Console.WriteLine(stateInfo);
        }

如果业务需要,该参数还可传入实体对象

4. 混合方法(Hybrid Approach)

使用混合方法执行 TAP 的好处是,可以隐藏业务实现细节,提供统一调用入口

4.1 定义混合方法
[HttpGet("HyBrid")]
        public Task<string> HyBrid([FromQuery]string value)
        {
            return  HyBridInternal(value);
        }

        private async Task<string> HyBridInternal(string value)
        {
            await Task.Run(() =>
            {
                Console.WriteLine(value);
            });

            return "Your Input:" + value;
        }

混合方法,不要被它的名头唬住,你可以把它看成一个方法重载,这样做的好处是,当发生异常是,你可以快速的定位到出现异常的方法,而不是任务

结束语

本文的内容只是上一篇文章的补充,所以这里就不在放入执行结果,但是示例代码还是一样的奉上

示例代码下载

https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.TaskSecond

本文分享自微信公众号 - DotNet程序园(dotnetblog),作者:梁规晓

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-01-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Asp.Net Core 轻松学-多线程之Task快速上手

        Task是从 .NET Framework 4 开始引入的一项基于队列的异步任务(TAP)模式,从 .NET Framework 4.5 开始,任何使用...

    梁规晓
  • Asp.Net Core 轻松学-多线程之取消令牌

        取消令牌(CancellationToken) 是 .Net Core 中的一项重要功能,正确并合理的使用 CancellationToken 可以让业...

    梁规晓
  • EF Core 实现读写分离的最佳方案

    公司之前使用Ado.net和Dapper进行数据访问层的操作, 进行读写分离也比较简单, 只要使用对应的数据库连接字符串即可. 而最近要迁移到新系统...

    梁规晓
  • 使用 python 快速搭建http服务 传输服务,下载服务器资源

    python2 使用 python -m SimpleHTTPServer 快速搭建http服务 python3 使用 python -m httpserv...

    98k
  • markdown语法简介

    子勰
  • 用SignalR 2.0开发客服系统[系列2:实现聊天室]

    前言 交流群:195866844 上周发表了 用SignalR 2.0开发客服系统[系列1:实现群发通讯] 这篇文章,得到了很多帮助和鼓励,小弟在此真心的感谢大...

    GuZhenYin
  • 扫雷游戏-c语言学习笔记

    Youngxj
  • Java实现动态代理的两种方式

    Java领域中,常用的动态代理实现方式有两种,一种是利用JDK反射机制生成代理,另外一种是使用CGLIB代理。

    朝雨忆轻尘
  • Android跨进程通信IPC之14——其他IPC方式

    前面几篇文章,我们介绍了IPC的基础知识和Binder机制,本篇文章主要讲解各种跨进程的通信方式。

    隔壁老李头
  • 用 Python 语言来写游戏

    每个程序员差不多都是从计算机爱好者开始的,尤其是那些令人心醉神迷的电脑游戏,不仅造就了整个游戏产业,推动了计算机行业软硬件的升级,而且吸引了大量的爱好者最终加入...

    宇相

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动