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

Posted xiaoxuzhi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊多线程哪一些事儿(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
为了更高的交流,欢迎大家关注我的公众号,扫描下面二维码即可关注,谢谢:

技术图片

以上是关于聊聊多线程哪一些事儿(task)之 二 延续操作的主要内容,如果未能解决你的问题,请参考以下文章

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

C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!

C#多线程和异步——Task和async/await详解

C# Task总结(异步操作+并行)

聊聊Netty那些事儿之Reactor在Netty中的实现(创建篇)

JavaFX 多线程之 TaskServiceScheduledService