聊聊多线程哪一些事儿(task)之 二 延续操作

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

时间: 2024-11-09 20:39:55

聊聊多线程哪一些事儿(task)之 二 延续操作的相关文章

聊聊多线程那一些事儿 之 四 经典应用(取与舍、动态创建)

hello task,咱们又见面啦!!前面已经通过三篇简单的文章,对多线程的创建.运行.阻塞.等待.取消.延迟操作.异步方法等相关的知识点,通过这一些介绍,现在上手写一个多线程就是分分钟的小事件.如果需要看前三排文章的小伙伴,可以点击下面链接快速阅读谢谢! 说了那么多后,我仔细想了一下,还是要来点实际的项目用例比较实在,那么我现在就讲我平时在项目中用常用的一些业务梳理处理,以供参考,写到不好勿喷,有更好的解决方式,欢迎交流.谢谢! 应用一.多线程的中的取与舍 还是用上几篇文章中的关于酒店客房的数

多线程系列(3)任务Task

虽然使用线程池ThreadPool让我们使用多线程变得容易,但是因为是由系统来分配的,如果想对线程做精细的控制就不太容易了,比如某个线程结束后执行一个回调方法.恰好Task可以实现这样的需求.这篇文章我从以下几点对Task进行总结. 认识Task Task的用法 认识Task Task类在命名空间System.Threading.Tasks下,通过Task的Factory返回TaskFactory类,以TaskFactory.StartNew(Action)方法可以创建一个新的异步线程,所创建的

2 Task中的延续和7种阻塞

1.wait using System; using System.Threading; using System.Threading.Tasks; namespace 多线程_List { class Program { static void Main(string[] args) { //Task中的延续和7种阻塞 //阻塞 wait 有点像thread的join 作用是Task执行完 这个wait之后的代码才可以执行 Task t1 = new Task(()=> { Thread.Sl

windows下mongodb基础玩法系列二CURD操作(创建、更新、读取和删除)

windows下mongodb基础玩法系列 windows下mongodb基础玩法系列一介绍与安装 windows下mongodb基础玩法系列二CURD操作(创建.更新.读取和删除) 简单说几句 在mongodb中3元素:db(数据库).collection(集合).document(文档) 其中collection类似于数据库中的表,document类似于行,这样一来我们就将内容对比起来记忆学习了. 数据格式 MongoDB documents是BSON格式(一种类json的一种二进制形式的存

Thinkphp入门 二 —空操作、空模块、模块分组、前置操作、后置操作、跨模块调用(46)

原文:Thinkphp入门 二 -空操作.空模块.模块分组.前置操作.后置操作.跨模块调用(46) [空操作处理] 看下列图: 实际情况:我们的User控制器没有hello()这个方法 一个对象去访问这个类不存在的方法,那么它会去访问”魔术方法__call()” 用户访问一个不存在的操作—>解决:给每个控制器都定义个_empty()方法来处理 第二个解决方法:定义一个空操作 [空模块处理] 我们使用一个类,但是现在这个类还没有被include进来. 我们可以通过自动加载机制处理__autoloa

sk_buff整理笔记(二、操作函数)

承接上一篇:sk_buff整理笔记(一.数据结构)这一篇要讲的是内核为sk_buff结构提供的一些操作函数. 第一.首先要讲的是sk_buff中的四大指针: 四大指针分别是:head.data.tail.end:这四个指针都是指向了数据区的,数据区中存放的是协议头和数据.head和end指针是每个数据包一样的,也是有这两个指针来确定数据区的大小的.最开始的时候head.data.tail三个指针都是指向一起的,当有协议数据加进来时才更改. 再来说下包的形成,首先是应用层的数据,然后到四层加上四层

java: Comparable比较器,定义二叉操作类

//定义二叉操作类 class BinaryTree{ class Node{ private Node left; //左指数 private Node right; //右指数 private Comparable data; public Node(Comparable<?> data) { this.data = data; } //二叉数据比较,大的放在右边,小的放在左边 public void addNode(Node newNode) { //放在左边 if( newNode.d

函数bsxfun,两个数组间元素逐个计算的二值操作

转自http://www.cnblogs.com/rong86/p/3559616.html 函数功能:两个数组间元素逐个计算的二值操作 使用方法:C=bsxfun(fun,A,B) 两个数组A合B间元素逐个计算的二值操作,fun是函数句柄或者m文件,也可以为如下内置函数: @plus 加@minus 减@times 数组乘<Simulink与信号处理>@rdivide 左除@ldivide 右除@power 数组幂乘@max 二值最大值@min 二值最小值@rem 余数@mod 求模@ata

学习Opencv 2.4.9(二) ---操作像素

作者:咕唧咕唧liukun321 来自:http://blog.csdn.net/liukun321 本质上说一张图像就是由数值组成的矩阵.Opencv 2.x由 cv::Mat 这个数据结构来表示一张图像.矩阵的每一个元素代表了一个像素.对于彩色图像而言矩阵的元素是一个三元数.对图像有了这个新的认识,下面可以试着借助opencv处理图像了. 先来看一下今天要处理的图像: 今天的主题是存取像素,首先来看一下如何存取像素值.其实对于像素值的操作都可以由cv::Mat类中成员直接或间接实现,cv::