聊聊多线程那一些事儿(task)之 一

  多线程,一个多么熟悉的词汇,作为一名程序员,我相信无论是从事什么开发语言,都能够轻轻松松说出几种实现多线程的方式,并且在实际工作种也一定用到过多线程,比如:定时器、异步作业等等,如果你说你没有用过多线程,我怀疑你是不是一名程序员,哈哈。

哈哈,言归正传,今天我们要说说c#中的多线线程哪一些事,当然c#在实现多线程上有多种方式,比如:Threads、Action、ThreadPool、Task、Parallel等,当然每一种方式都用其优点和缺点,也有其应用场景,在此不一一说明,今天我们主要以task为例,来一起聊聊task的使用,也就当着一次自我总结提炼罢了。

为什么要用多线程

其实我们在实际使用过程中,使用多线程的目的其实即使为了实现异步+并行,异步:是相对同步的,同步就是一个流程安装一个流程执行完毕,异步就是在不影响主流程的执行同时,可以执行其他流程,这也就是达到了几个逻辑并行执行的效果。当然了,不是说异步就完全是独立执行,相互间就没有关联关系,其实在异步的同时,也可以在特定节点等待阻塞等待异步结果啦。说了半天废话,不要走开,主题才刚刚开始,下面以实际例子来演绎task的实际使用吧!

如何创建和运行一个task

微软就是那么6逼,在创建和执行一个task时,都给大家提供了多种方式来实现,大家可以根据其具体的使用场景和习惯来选择最适合的方式,下面通过代码来简单说明如下的三种实现方式:

/// <summary> /// 简单的task创建方式演示 /// </summary> private static void TaskCreatFun() {     // 其一、通过传统的 new 方式来实例化一个task对象,这种方式需要手动通过start来启动     Task newTask = new Task(() =>     {         Thread.Sleep(1000);         Console.WriteLine($"Hello Engineer, 我是 new 的一个task,线程ID:Thread.CurrentThread.ManagedThreadId}");     });     // 启动 tsak     newTask.Start();     // 其二、通过工厂 factory 来生成一个task对象,并自启动     Task factoryTask = Task.Factory.StartNew(() =>     {         Thread.Sleep(1000);         Console.WriteLine($"Hello Engineer, 我是 factory 生产 的一个task,线程ID:Thread.CurrentThread.ManagedThreadId}");     });     // 其三、通过 Task.Run(Action action) 来创建一个自启动task     Task runTask = Task.Run(() =>     {         Thread.Sleep(1000);         Console.WriteLine($"Hello Engineer, 我是 Task.Run 创建一个自启动task,线程ID:Thread.CurrentThread.ManagedThreadId}");     });     runTask.RunSynchronously();     Console.WriteLine($"Hello Engineer, 我是主线程啦!线程ID{Thread.CurrentThread.ManagedThreadId}"); }

代码的执行结果:很容易看出,task执行阻塞主线程,并且几个task并行执行。

如何创建一个带有返回值的task

在上面的代码实例种,我们以不同的方式创建并运行了一个多线程,但是这几个task都是没有返回值的,这样的task适用于:独立执行的一个子业务,完全不关心其执行结果。但是我们在实际使中,往往会需要关系其执行结果的。

以一个实际的业务场景来说明:比如,我们在一个酒店预订系统中,需要实时到不同的第三接口实时查询某一个酒店的某一客房在最新状态,比如有3个接口渠道:携程、艺龙、去哪儿,该如何实现呢?

首先,如果我们采用串行的方式一个一个的去取数据,那样估计你的系统慢到不会有人用的,所以我们第一个想到的是,采用task来并行获取,并返回获取到的结果值,下面简单模拟一下代码实现:

/// <summary> /// 获取最新的客房信息 /// </summary> /// <returns>客房信息集合</returns> private static List<string> GetHotelRoomInfro() {     // 模拟存储获取到的酒店客房数据集合     List<string> listHotelRoomInfro = new List<string>();     Console.WriteLine("下面通过3个task,并行的到不同接口方获取实时的客房信息:");     Console.WriteLine("");     // 在此我也分别对3种不同渠道,采用3种不同的方式来实现     // 其一、通过传统的 new 方式来实例化一个task对象,获取 携程 的客房数据     Task<string> newCtripTask = new Task<string>(() =>     {        // 具体获取业务逻辑处理...         Thread.Sleep(new Random().Next(100, 1000));         Console.WriteLine("携程 内部处理完毕!");         return "我是来自 携程 的最新客房信息";     });     // 启动 tsak     newCtripTask.Start();     // 其二、通过工厂 factory 来生成一个task对象,并自启动:获取 艺龙 的客房数据     Task<string> factoryElongTask = Task<string>.Factory.StartNew(() =>     {         // 具体获取业务逻辑处理...         Thread.Sleep(new Random().Next(100, 1000));         Console.WriteLine("艺龙 内部处理完毕!");         return "我是来自 艺龙 的最新客房信息";     });     // 其三、通过 Task.Run(Action action) 来创建一个自启动task:获取 去哪儿网 的客房数据     Task<string> runQunarTask = Task<string>.Run(() =>     {         // 具体获取业务逻辑处理...         Thread.Sleep(new Random().Next(100, 1000));         Console.WriteLine("去哪儿网 内部处理完毕!");         return "我是来自 去哪儿网 的最新客房信息";     });     // 分别打印不同渠道的客房数据     Console.WriteLine(newCtripTask.Result);     Console.WriteLine(factoryElongTask.Result);     Console.WriteLine(runQunarTask.Result);     Console.WriteLine("");     Console.WriteLine("所有接口方的最新客房数据获取完毕!");     return listHotelRoomInfro; }

执行结果:

其实通过上面的执行结果,我们不难看出以下几点

1、task虽然是异步线程,但是可以有返回值的

2、task的返回值获取方式为:task.Result

3、在通过task.Result获取返回值时,会阻塞主线程,其实也不难理解,你要等待处理结果,肯定会阻塞等待啦!

task可以同步执行吗?

通过上面的实际代码测试,我们知道task都是异步执行,那么有人会问,task可以实现同步执行吗?不急,强大的微软也想到了这个问题,于是乎,提供了 task.RunSynchronously() 来同步执行,但是这种方式执行,只有通过new 实例化的task才有效,原因也很简单,其他两种方式创建task都已经自启动执行了,不可能在来一个同步启动执行吧,嘿嘿。下面我们用代码来演示:

/// <summary> /// 通过RunSynchronously 实现task的同步执行 /// </summary> private static void TaskRunSynchronously() {     Console.WriteLine("主线程开始执行!");     Task<string> task = new Task<string>(() =>     {         Thread.Sleep(100);         Console.WriteLine("Task执行结束!");         return "";     });     /// task.Start();     /// task.Wait();     // 获取执行结果,会阻塞主流程     // string result = task.Result;     //同步执行,task会阻塞主线程     task.RunSynchronously();     Console.WriteLine("执行主线程结束!");     Console.ReadKey(); }

执行结果:很明显主线程阻塞等待task同步执行。

task同步执行,出了上面的实现方式,其实我们也可以通过task.wait()来变相的实现同步执行效果,当然也可以用task.Result来变现的实现,原理很简单,因为wait()和Result都是要阻塞主流程,直到task执行完毕,是不是有异曲同工之妙呢!以代码为例:

通过task.wait()实现,只需要对上面的代码做一个简单的调整,如下:其最终的效果一样:

/// <summary> /// 通过RunSynchronously 实现task的同步执行 /// </summary> private static void TaskRunSynchronously() {     Console.WriteLine("主线程开始执行!");     Task<string> task = new Task<string>(() =>     {         Thread.Sleep(100);         Console.WriteLine("Task执行结束!");         return "";     });      task.Start();      task.Wait();     // 获取执行结果,会阻塞主流程     // string result = task.Result;     // 同步执行,task会阻塞主线程     // task.RunSynchronously();     Console.WriteLine("执行主线程结束!");     Console.ReadKey(); }

执行结果:

通过task.Result 实现,前提是task一定要有返回值,如下:其最终的效果一样:

/// <summary> /// 通过RunSynchronously 实现task的同步执行 /// </summary> private static void TaskRunSynchronously() {     Console.WriteLine("主线程开始执行!");     Task<string> task = new Task<string>(() =>     {         Thread.Sleep(100);         Console.WriteLine("Task执行结束!");         return "";     });      task.Start();     /// task.Wait();     // 获取执行结果,会阻塞主流程     string result = task.Result;     // 同步执行,task会阻塞主线程     // task.RunSynchronously();     Console.WriteLine("执行主线程结束!");     Console.ReadKey(); }

执行效果也和上面的两种方式一样。

当然我还可以通过task.IsCompleted来变现实现,在此就不在细说,简单一个代码示意即可:while (!task.IsCompleted){}

当然我上面说的几种实现同步的方式,只是为了拓展一下思路,不一定都是最优方案。

Task的Wait、WaitAny、WaitAll方法介绍

task的基本创建和用法,上面都做了简单的介绍,但是在我们实际业务场景中,往往不是那么简单的单纯实现。比如:还是刚刚上面的那个酒店信息获取为例,现在新的需求是:3个渠道的接口实时数据,我们只需要获取到其中的一个就立即返回会用户,避免用户等待太久,那么这个时候task.WaitAny就派上用场了,WaitAny就是等待一组tsak集合中,只要有一个执行完毕就不在等待,与之对应的是WaitAll需要等待一组tsak集合中所有tsak都执行完毕,当然了Wait是针对一个task的,等待本身执行完成,上面的模拟同步执行已经说了,就不在啰嗦。

/// <summary> /// 获取最新的客房信息(只需要获取到一个即可) /// </summary> /// <returns>客房信息集合</returns> private static List<string> GetOneHotelRoomInfro() {     // 模拟存储获取到的酒店客房数据集合     List<string> listHotelRoomInfro = new List<string>();     Console.WriteLine("下面通过3个task,并行的到不同接口方获取实时的客房信息:");     Console.WriteLine("");     // 在此我也分别对3种不同渠道,采用3种不同的方式来实现     // 其一、通过传统的 new 方式来实例化一个task对象,获取 携程 的客房数据     Task newCtripTask = new Task(() =>     {         // 具体获取业务逻辑处理...         Thread.Sleep(new Random().Next(100, 1000));         listHotelRoomInfro.Add("我是来自 携程 的最新客房信息");     });     // 启动 tsak     newCtripTask.Start();     // 其二、通过工厂 factory 来生成一个task对象,并自启动:获取 艺龙 的客房数据     Task factoryElongTask = Task.Factory.StartNew(() =>     {         // 具体获取业务逻辑处理...         Thread.Sleep(new Random().Next(100, 1000));         listHotelRoomInfro.Add("我是来自 艺龙 的最新客房信息");     });     // 其三、通过 Task.Run(Action action) 来创建一个自启动task:获取 去哪儿网 的客房数据     Task runQunarTask = Task.Run(() =>     {         // 具体获取业务逻辑处理...         Thread.Sleep(new Random().Next(100, 1000));         listHotelRoomInfro.Add("我是来自 去哪儿网 的最新客房信息");     });     // 只需要等待一个有返回即可     Task.WaitAny(new Task[] { newCtripTask, factoryElongTask, runQunarTask });     // 等待所有接口数据返回     // Task.WaitAll(new Task[] { newCtripTask, factoryElongTask, runQunarTask });     Console.WriteLine("已经有接口数据返回!");     foreach (var item in listHotelRoomInfro)     {         Console.WriteLine($"返回的接口数据为:{item}");     }     Console.WriteLine("主线程执行完毕!");     Console.ReadKey();     Console.WriteLine("");     return listHotelRoomInfro; }

上面是演示了WaitAny的执行结果,至于WaitAll就不在演示了,一看便知

好了,今天时间差不多了,就介绍到儿了,明天我们在来研究:

1、task的延续操作(WhenAny/WhenAll/ContinueWith)

2、任务取消(CancellationTokenSource)

3、实际案例分析

(0)

相关推荐

  • C#多线程编程(二)线程池与TPL

    一.直接使用线程的问题 每次都要创建Thread对象,并向操作系统申请创建一个线程,这是需要耗费CPU时间和内存资源的. 无法直接获取线程函数返回值 无法直接捕捉线程函数内发生的异常 使用线程池可以解 ...

  • 一文说通C#中的异步编程

    天天写,不一定就明白. 又及,前两天看了一个关于同步方法中调用异步方法的文章,里面有些概念不太正确,所以整理了这个文章.   一.同步和异步. 先说同步. 同步概念大家都很熟悉.在异步概念出来之前,我 ...

  • 线程(一)——线程,线程池,Task概念+代码实践

    目录 线程(一)--线程,线程池,Task概念+代码实践 12.1 通过TPL进入线程池 12.2 不同过TPL进入线程池 12.3 线程池优化 12.1.1 Task异常捕获 12.2.1 Queu ...

  • 聊聊医学专业那些事儿

    医生向来是一个受人尊敬,社会地位高,收入高的一个职业.前几年因为医患关系紧张,读医学的人少了很多.大家甚至说,劝人学医,天打雷劈.这两年,大家又慢慢醒悟过来,发现医生还是一个非常好的职业,所以考医热又 ...

  • 聊聊大学名字那些事儿:河北工大不在河北,西南医科大学改名两次,电子科大的英文名有玄机……

    最近,教育部发了个文件,关于<高等学校命名暂行办法>.看了这些规定,想起了学校名字的一些趣事,跟大家聊聊. 河北工业大学 河北工业大学是河北省唯一一所211大学,不过,很多人想不到的是,河 ...

  • 展览博见丨窖藏·事——聊聊宋瓷那些事儿

    "湛碧平湖 千峰翠色--四川遂宁金鱼村南宋窖藏瓷器精品展"已于7月6日在中国园林博物馆四号临展厅开幕,100件(套)宋代瓷器精品来京展出,稀世珍品,难得一见! 看过上一期的朋友是否 ...

  • 520,聊聊临床试验的那些事儿 | 国际临床试验日

    今天是520,在充满粉红泡泡的浪漫节日里,大家可别忘了它还有另外一个值得纪念的日子--第17个国际临床试验日. 今天,跟着江苏省人民医院李薇副教授一起谈谈"临床试验"的那些事儿. ...

  • 深夜话题:聊聊越剧票房那些事儿

    四月,一年中的演出旺季,各地越剧演出如火如荼地进行中,越剧之家的朋友圈又开始刷屏了:有各种演出花絮,有后台探班,有各种打卡合影,似乎越剧圈走出了疫情的阴霾,迎来了市场的井喷. 的确,这个四月,院团很忙 ...

  • 复工加油!和您聊聊副业收入那些事儿

    不知大家是否已经全面复工了,我现在还在家里等待上班的通知.我们国家的疫情已经逐渐控制住了局面,截至昨天本土确诊已经降至900例以下,但是世界范围还是大面积的爆发.我虽然不会失业,但是对今年也不是很抱希 ...

  • 快递藏“毒”问题突出!检察官和你聊聊毒品犯罪那些事儿

    根据公安部公布的<2019年中国毒品形势>显示,截至2019年底,中国现有吸毒人员214.8万名.全年共查获吸毒人员61.7万人次,其中新发现吸毒人员22.3万名.近年来,我国政府大力禁毒 ...

  • 女同胞,这个三八妇女节我们想跟你聊聊妇科肿瘤那些事儿

    *仅供医学专业人士阅读参考 精彩街头采访,来看看你能回答出几个问题! 在这个女性拥有越来越多角色的时代,来自工作.家庭.社会等等的压力也随之而来,且无处不在.纪录片<生生>当中的一个年轻乳 ...

  • 咱们一起聊聊关于考研那些事儿

    咱们一起聊聊关于考研那些事儿