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; }
混合方法,不要被它的名头唬住,你可以把它看成一个方法重载,这样做的好处是,当发生异常是,你可以快速的定位到出现异常的方法,而不是任务
结束语
本文的内容只是上一篇文章的补充,所以这里就不在放入执行结果,但是示例代码还是一样的奉上
示例代码下载
上一篇: 这份厚礼,岳母一定会收下的
下一篇: 投资的不同见解