hello,又见面啦,昨天我们简单的介绍了如何去创建和运行一个task、如何实现task的同步执行、如何阻塞等待task集合的执行完毕等待,昨天讲的是task的最基本的知识点,如果你没有看昨天的博客,也不要急,你可以点击下面的地址, 聊聊多线程哪一些事儿(task)之 一),先看看后,在回到这儿来继续交流学习今天的文章,谢谢!
今天主要和大家交流分享的是:task的延续操作、task的异步取消、异步方法等知识点,希望通过本篇文章,能够给你带来一点点帮助我就高兴的不要不要的啦。当然啦,既然是交流,如果我有什么说的不对,或者说的不好的地方,大家多多指点,多多包涵,如果能够得到大牛的指点,我也会高兴的合不拢嘴,谢谢。好了,不废话了,言归正传,继续今天的分享加交流。
Task延续操作之WhenAny、WhenAll、ContinueWith
上一篇文章我们已经知道可以通过task.wait/task.WaitAny/task.WaitAll,等方法来实现等待一个tsak或者一组task的执行完毕,这一个方法都会阻塞主线程(如果没有看一篇文章的请单击查看:聊聊多线程哪一些事儿(task)之 一),也就是这一些操作都是主流程的一个必然环节,但是我们在实际项目中,也还会遇到这样的场景,那就是主流程根本不关心task的执行结果,但是task执行完毕后,需要执行一个其他的子业务,那么这个时候WhenAny、WhenAll、ContinueWith就派上用场了,这几个方法也就专门是为这样的场景而存在的。哈哈,说了这么多,是不是觉得有点抽象,有点云里雾里的感觉,说实话,我自己都觉得说的太空洞,还不如来一个实际的场景+代码实例,这样整的更明白。
实际业务场景:我想了半天,到得用什么样的业务场景比较合适呢,最终决定还是以昨天酒店客房数据查询为例进行为例。用户在线预订酒店时,由于真正的客房预订是需要实时的到第三接口平台预订,所以用户在自己系统下单后,并不代表真正的酒店预订成功,真正的酒店预订成功,是需要通过接口到第三方系统下单成功才算真正的预订成功,并且一个平台对接的接口都会有多个,平台最终会比价后,选择一个平台利用最大化的接口下单。这样以来,系统不可能让用户等待到第三方接口下单成功后,在返回的用户吧,这样用户是没有那么好的心情的来等待的,并且这样也很容易超时的,所以在实际项目处理上,是需要将本系统下单和第三接口预订两个步骤解耦,实现异步预订,其大致的业务逻辑是这样的。
第一步:用户在系统发起酒店预订请求
第二步:本系统下单成功,并返回给用户
第三步:开启一个异步线程,到接口方下单
异步线程的处理逻辑大概是:
其一:具有该客房的接口方各开启一个异步线程
其二:每一个线程的具体逻辑是,根据客房信息查询具体的客房信息(客房状态、价格、服务等)
其三:当每一个异步线程都执行完毕后,对获取到接口数据进行对比分析,选择一个最优的接口方进行预订
其四:预订成功后,并发送一个消息给客户
好了,下面用一个简单的实例代码来模拟上面的处理逻辑和流程
/// <summary> /// 酒店在线预订操作 /// </summary> /// <param name="hotelBookInfo">用户预订的酒店信息</param> /// <returns>预订结果</returns> private static object HotelBook(object hotelBookInfo) { Console.WriteLine("用户发起了酒店预订请求!"); Console.WriteLine(""); // 第一步:首先本系统落地酒店预订相关操作(数据校验、数据落地、日志记录等等) // ---此处省略具体的业务逻辑代码,根据业务需要自由发挥 // 第二步:根据具有该客房的酒店接口商,开启异步线程预订酒店 //(假设第一步操作都成功的,并且 携程和艺龙都有该客房) // 模拟存储获取到的酒店客房数据集合 List<string> listHotelRoomInfro = new List<string>(); // 其一、通过传统的 new 方式来实例化一个task对象,获取 携程 的客房时时数据 Task newCtripTask = new Task(() => { // 具体获取业务逻辑处理... Thread.Sleep(new Random().Next(100, 1000)); Console.WriteLine("携程 接口数据获取完毕!"); Console.WriteLine(""); listHotelRoomInfro.Add("我是来自 携程 的最新客房信息,该客房可预订,预订价格为:100元"); }); // 启动 tsak newCtripTask.Start(); // 其二、通过工厂 factory 来生成一个task对象,并自启动:获取 艺龙 的客房数据 Task factoryElongTask = Task.Factory.StartNew(() => { // 具体获取业务逻辑处理... Thread.Sleep(new Random().Next(100, 1000)); Console.WriteLine("艺龙 接口数据获取完毕!"); Console.WriteLine(""); listHotelRoomInfro.Add("我是来自 艺龙 的最新客房信息,该客房可预订,预订价格为:99元"); }); // 其三:通过 Task.WhenAll() 来执行 携程和艺龙的客房数据获取结果的后续处理 // Task.WhenAll() 可以用 Task.Factory.ContinueWhenAll()来代替,两种的效果是一样的 Task.Factory.ContinueWhenAll(new Task[] { newCtripTask, factoryElongTask }, (t) => { }); Task.WhenAll(newCtripTask, factoryElongTask).ContinueWith((t) => { Console.WriteLine("所有接口数据获取完毕,现在进行最优选择!"); Console.WriteLine(""); // 对比 所有接口获取的实时数据,找到一个平台利益最大化的接口预订 // 具体的规则,每个平台都有自己的规则,在此我们简单的以价格最低为准,具体的对比逻辑就不在写了 // 根据数据分析,最终艺龙的价格最低,所以直接预订艺龙的数据 Console.WriteLine("艺龙接口最优,现在调用艺龙在线预订接口!"); Console.WriteLine(""); // 模拟调用艺龙的酒店预订接口,进行酒店预订 Thread.Sleep(new Random().Next(100, 1000)); // 预订成功后,系统数据落地的相关业务操作..... Console.WriteLine("艺龙在线预订成功,开始后续业务流程处理!"); }); // 第三步步:返回用户,下单成功 Console.WriteLine("您好,你的酒店下单成功,具体的预订结果等待第三方酒店提高商的返回结果为准!谢谢"); Console.WriteLine(""); return "构建下单成功相关信息"; }
执行结果:
通过上面的执行结果,我们可以得出以下几点:
其一、WhenAll里面的逻辑是在所有tsak都执行完毕后,在执行
其二、whenAll的执行,不会影响主线程的执行
其三、其实简单的理解,WhenAll可以理解为一个task组的异步回调
好了,详细的举例说了whenAll的使用,至于 WhenAny 使用就不在详细说明了,其实从字面意思都能够看明白啦,就是只要所有的task集合中,只有有一个task执行完成,就在执行whenAny里面的逻辑,也就是说,wenAll和whenAny的唯一区别就是:前者是要所有task都执行完毕才执行,后者只需要有一个执行完毕就执行里面的逻辑。
其实WhenAny 的有实际应用场景也是很多的,比如,一个具有竞争的业务逻辑中,最终只选择一个效率对快的一个,那么WhenAny就能够发挥其作用了。
Task.WhenAll与Task.Factory.ContinueWhenAll
Task.WhenAny 与Task.Factory.ContinueWhenAny
这两者是一个成对的等效操作,其实你阅读两个的底层源码实现,你会发现,其实最终第底层实现都是调用的TaskFactory中的同一方法实现,所以就不在啰嗦的写一次Task.Factory.ContinueWhenAll的场景应用,需要阅读的底层源码的,可以查看下面地址进行阅读:https://blog.csdn.net/weixin_33701294/article/details/85958795
好了,今天就写到这儿了,我仔细看了一下,文章篇幅已经够多了,本篇文章就不在介绍 task的异步取消、异步方法这两个知识点了,明天我会在单独一篇文章专门来介绍这两个知识点,想继续看明天的后续文章的小伙伴们,点关注哦,明天一定补上,谢谢!
END
为了更高的交流,欢迎大家关注我的公众号,扫描下面二维码即可关注,谢谢:
原文地址:https://www.cnblogs.com/xiaoXuZhi/p/XYH_tsak_WhenAll.html