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

hello task,咱们又见面啦!!前面已经通过三篇简单的文章,对多线程的创建、运行、阻塞、等待、取消、延迟操作、异步方法等相关的知识点,通过这一些介绍,现在上手写一个多线程就是分分钟的小事件。如果需要看前三排文章的小伙伴,可以点击下面链接快速阅读谢谢!

说了那么多后,我仔细想了一下,还是要来点实际的项目用例比较实在,那么我现在就讲我平时在项目中用常用的一些业务梳理处理,以供参考,写到不好勿喷,有更好的解决方式,欢迎交流。谢谢!

应用一、多线程的中的取与舍

还是用上几篇文章中的关于酒店客房的数据来为例分析,假设系统同时对接了x程、y龙、q哪三家接口数据,用户进入到某一个酒店预订页面,系统需要实时的到第三方取该酒店对应房间的实时动态数据呈现给用户,但是在这个过程中,不能让用户等待的太久,并且能够尽可能多的提供多渠道给用户选择,那么这个时候该如何去实现这个需求呢?

做过聚合平台的同学,无论是酒店、机票、咨询等等,或多或少都会遇到这样类似的业务场景,下面分享一下,我们平时是怎么实现。

简单的说就是一个取与舍的逻辑,你想啊,不同的接口方,接口的查询效率不尽相同,并且同一接口在不同时间处理时间也不尽相同,遇到某一些特殊情况,有可能一个接口数据需要等待5-10s甚至更久才能取出数据,这个时候你不可能让用户也等待这么久吧,如果是这样,估计用户早吓跑了。况且这样等待下去,也会对系统带来很大的压力,尤其是在做活动高峰期时,那直接就是系统的一个瓶颈了。

为了到达这一目的,那么在实现上,我们首先应该想到的是,在约定时间内未返回数据的接口,那么我们直接放弃,只展示给用户已取到的数据即可。那在技术上该如何实现呢?

在实现上,有了Task,一切都变得那么轻松啦,因为Task.Wait方法已经为我们考虑到了这样的场景,直接使用即可。

我们看看Task.Wait的几个重载吧:

看到了吧,在重载方法中,有一个timeOut字段,该字段就是用于舍弃超时未处理完成的线程任务。那具体该怎么用呢?直接上代码吧!

static void Main(string[] args)
{
    Console.WriteLine("开始获取酒店数据......");
// 获取最新的客房信息(假设最大等待时间为1S)
    List<string> listHotelRoomInfro = GetHotelRoomInfro(1000);
    Console.WriteLine("获取酒店数据获取完毕,获取到的数据为:");
foreach (var item in listHotelRoomInfro)
    {
        Console.WriteLine(item);
    }
    Console.ReadLine();
 }

/// <summary>
/// 获取最新的客房信息
/// </summary>
/// <param name="maxWaitTimes">最大获取时间(也就是舍弃时间),单位毫秒</param>
/// <returns>客房信息集合</returns>
private static List<string> GetHotelRoomInfro(int maxWaitTimes)
 {
// 模拟存储获取到的酒店客房数据集合
     List<string> listHotelRoomInfro = new List<string>();
// 在此我也分别对3种不同渠道,采用3种不同的方式来实现
// 其一、通过传统的 new 方式来实例化一个task对象,获取 携程 的客房数据
     Task newCtripTask = new Task(() =>
    {
// 具体获取业务逻辑处理...
         Thread.Sleep(new Random().Next(100, 999));
        listHotelRoomInfro.Add("我是来自 携程 的最新客房信息");
    });
// 启动 tsak
     newCtripTask.Start();
// 其二、通过工厂 factory 来生成一个task对象,并自启动:获取 艺龙 的客房数据
     Task factoryElongTask = Task.Factory.StartNew(() =>
     {
// 具体获取业务逻辑处理...
         Thread.Sleep(new Random().Next(555, 1500));
         listHotelRoomInfro.Add("我是来自 艺龙 的最新客房信息");
     });
// 其三、通过 Task.Run(Action action) 来创建一个自启动task:获取 去哪儿网 的客房数据
     Task runQunarTask = Task.Run(() =>
     {
// 具体获取业务逻辑处理...
         Thread.Sleep(new Random().Next(1100, 2000));
         listHotelRoomInfro.Add("我是来自 去哪儿网 的最新客房信息");
     });
// 等待获取接口,阻塞主流程,如果 在 指定的时间 maxWaitTimes未返回数据的接口方直接舍弃掉
     Task.WaitAll(new Task[] { newCtripTask, factoryElongTask, runQunarTask }, maxWaitTimes);
return listHotelRoomInfro;
 }
运行结果:

通过上面的运行结果,我们发现在1s内,只有携程返回了数据,那么最终也就只返回给用户携程的数据。这个实例就这样实现了,当然,wi相信你或许会有更好的实现,欢迎一起交流与学习,谢谢

应用二:动态创建多线程均批处理

?在实际项目中,我们会遇到需要根据待处理任务数量,动态创建多线程来最优化分批处理。哈哈哈是不是说的有点空洞,云里雾里的,不急不急,下面来一个实际场景,一看你就明明白白啦。

?需求:还是以酒店数据同步为例。具体需求是这样的:

?1.手动批选择指定酒店,单次最多可选择500个

?2.系统需要以最快的方式同步会接口方的最新数据

?哈哈哈看了需求是不是觉得很简单,就两句话,那该如何来实现这个需求呢?

?也许你会说,简单啊,根据所选择的酒店以次排队同步数据就对了嘛!这样可不行哦,如果用户选择了500个酒店,假设每个酒店数据同步需要2秒,那么500条数据都需要1000秒,那需要接近20分钟才处理完,这可不是时间最优啊!

?那你也要你还会说那就直接每一个酒店都开一个线程来处理,这样速度是不是够快啦!嗯你说的没错,这样速度是上去了,但是呢,如果用户选择500个酒店,那么就需要创建500个线程,也就是500个并发,这样会有什么问题呢?第一.你自己的服务器扛的住吗?第二.如果你服务器扛的住,那么接口允许你这么干嘛?估计早都把你拉黑名单了。所以这也不是最优方案。

?哈哈,说了那么多,那么具体该如何来实现呢?其实也简单,我想说的方案就去以上两两种方案的一个折中方案。动态的根据资源等多因素动态创建合理的线程,然后在并行处理。来来,直接上代码!

class Program
 {
     /// <summary>
     /// 最多允许创建的线程数,可以通过配置文件来配置
     /// 具体的值也该是由:服务器资源+接口限制等多因数来确定的
     /// </summary>
     public static int maxThread = 10;
     static void Main(string[] args)
     {
         // 假设选择处理20条酒店数据
         List<string> listHotel = new List<string>();
         for (int i = 0; i < 20; i++)
         {
             listHotel.Add($"我是酒店{(i + 1).ToString().PadLeft(2, ‘0‘)}");
         }

         // 创建Tsak处理酒店数据同步
         AsyncDynamicSynchronouslyHotel(listHotel);

         Console.ReadLine();
     }

     /// <summary>
     /// 根据酒店数据量,动态创建Tsak来处理酒店数据同步
     /// </summary>
     /// <param name="listHotel">待处理的酒店数据列表</param>
     private static void AsyncDynamicSynchronouslyHotel(List<string> listHotel)
     {
         object hotelTaskLock = new object();

         // 先做一个非空判断
         if (listHotel == null || listHotel.Count < 1)
         {
             return;
         }

         // task线程数组
         List<Task> taskList = new List<Task>();

         // 首先根据资源数据量+最大允许线程数来确定需要开启的线程
         int taskCount = listHotel.Count < maxThread ? listHotel.Count : maxThread;

         // 创建指定是task线程数
         for (int i = 0; i < taskCount; i++)
         {
             // 创建一个task线程
             taskList.Add(new Task(() =>
             {
                 while (listHotel != null && listHotel.Count > 0)
                 {
                     // 给该线程分配一个酒店处理任务
                     string hotelInfro = string.Empty;

                     // 线程通过,加一个资源锁
                     lock (hotelTaskLock)
                     {
                         // 在获取到锁后,还需要做一个资源判断,避免获取到锁后,资源以及被消耗完毕
                         if (listHotel != null && listHotel.Count > 0)
                         {
                             hotelInfro = listHotel[0];
                             listHotel.Remove(hotelInfro);
                         }
                     }

                     // 开始模拟真正的数据同步操作
                     if (!string.IsNullOrEmpty(hotelInfro))
                     {
                         Thread.Sleep(1000);
                         Console.WriteLine($"我是线程ID{Thread.CurrentThread.ManagedThreadId.ToString()  },完成酒店【{hotelInfro}的数据同步处理");
                     }
                 }
             }));

             // 启动线程
             taskList[i].Start();
         }
     }
 }
 

运行结果:

设置最多开5个线程

设置对多开10个线程

?这样就达到了自动的动态控制线程创建,当然这个里面还涉及到了线程同步等问题处理

总结:

?通过本篇文章,和大家分享了多线程的取与舍,多线的动态创建等等,在实际工作过程中,或多或多我们都会遇到这样的场景,希望能够有点帮助    ?

?好了,今天就说在这儿了,有什么问题,大家可以多多交流,最后祝大家元旦快乐

猜您喜欢:

 第一篇:聊聊多线程哪一些事儿(task)之 一创建运行与阻塞

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

 第三篇:聊聊多线程那一些事儿(task)之 三 异步取消和异步方法

END
为了更高的交流,欢迎大家关注我的公众号,扫描下面二维码即可关注,谢谢:

原文地址:https://www.cnblogs.com/xiaoXuZhi/p/XYH_Task_o4.html

时间: 2024-11-06 11:36:31

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

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

hello,又见面啦,昨天我们简单的介绍了如何去创建和运行一个task.如何实现task的同步执行.如何阻塞等待task集合的执行完毕等待,昨天讲的是task的最基本的知识点,如果你没有看昨天的博客,也不要急,你可以点击下面的地址, 聊聊多线程哪一些事儿(task)之 一),先看看后,在回到这儿来继续交流学习今天的文章,谢谢! 今天主要和大家交流分享的是:task的延续操作.task的异步取消.异步方法等知识点,希望通过本篇文章,能够给你带来一点点帮助我就高兴的不要不要的啦.当然啦,既然是交流,

javascript学习笔记(四):事件处理函数和动态创建html标记。

1 HTML的事件属性  全局事件属性:HTML 4 增加了使事件在浏览器中触发动作的能力,比如当用户点击元素时启动 JavaScript. a. Window 事件属性,针对 window 对象触发的事件(应用到 <body> 标签),常用的为onload. b. Form事件,由 HTML 表单内的动作触发的事件(应用到几乎所有 HTML 元素,但最常用在 form 元素中):常用的为onblur.onfocus.onselect.onsubmit. c. keybord事件 d.Mous

iOS多线程的初步研究(四)-- NSTimer

 iOS多线程的初步研究(四)-- NSTimer 原文地址  http://www.cnblogs.com/sunfrog/p/3243230.html 理解run loop后,才能彻底理解NSTimer的实现原理,也就是说NSTimer实际上依赖run loop实现的. 先看看NSTimer的两个常用方法: + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelec

C++--第24课 - 专题四经典问题解析

第24课 - 专题四经典问题解析 1. 历史的痕迹 #include <cstdlib> #include <iostream> using namespace std; template<class T>  //以前是用typename定义,现在是用class定义 T Minus(T a, T b) { return a - b; } template<class T>  //类模板 class Add { public: T add(T a, T b)

Python爬虫实战四之抓取淘宝MM照片

福利啊福利,本次为大家带来的项目是抓取淘宝MM照片并保存起来,大家有没有很激动呢? 最新动态 更新时间:2015/8/2 最近好多读者反映代码已经不能用了,原因是淘宝索引页的MM链接改了.网站改版了,URL的索引已经和之前的不一样了,之前可以直接跳转到每个MM的个性域名,现在中间加了一个跳转页,本以为可以通过这个页面然后跳转到原来的个性域名,而经过一番折腾发现,这个跳转页中的内容是JS动态生成的,所以不能用Urllib库来直接抓取了,本篇就只提供学习思路,代码不能继续用了. 之后博主会利用其它方

Hibernate 原汁原味的四种抓取策略(转)

原文出处:http://www.cnblogs.com/rongxh7/archive/2010/05/12/1733088.html     尊重原作者,访问原创地址 最近在研究 Hibernate 的性能优化的时候碰到了"抓取策略", 由于以前没有详细的研究过, 所以到处找资料, 但是无论从一些讲 Hibernate 书籍,还是他人 Blog 中都没有找到详细 介绍 Hibernate 文档中所说的原汁原味的抓取策略, 综合懒加载等等特性混在了一起, 所 以在这自己在借鉴了他人的基

BMDThread控件动态创建多线程示例

http://www.cnblogs.com/railgunman/archive/2010/12/08/1900688.html BMDThread控件是一套相当成熟的线程控件,使用它可以让你快速的创建.管理线程.    可以到CSDN或者盒子上下载BMDThread控件.    下面我们用多线程模拟客户端发送文件的例子来简单认识一下它.    在窗体中放置一个TIDClient,TBMDThread,TBMDThreadGroup.三个TEdit,两个按钮(开始线程,结束线程),一个MEMO

delphi 多线程 动态创建ADO

delphi 在多线程中动态连接ADO控件 异常:exception class EOleSysError with message '尚未调用CoInitialize' 如果是使用多线程的话那就在 Execute事件的开头加上 CoInitialize(nil); 结尾加上CoUninitialize() unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Fo

JavaFX战旗类游戏开发 第四课 属性框和菜单的创建

上一课中,我们创建了游戏角色.这节课中,我们将会创建菜单,以便后面使用. 由于只是Demo,我创建的是最简单的形式,如下图所示: 基于游戏开发中的UI控件通常需要有事件(比如图中的移动,攻击,待机,是有事件处理的),我们应该首先创建自己的文字控件. 文字控件代码如下: import com.sun.javafx.tk.FontMetrics; import com.sun.javafx.tk.Toolkit; import javafx.scene.canvas.GraphicsContext;