异步/多线程/任务/并行编程之一:如何选择合适的多线程模型?
发布时间:2023-12-17 01:03:21
异步、多线程、任务、并行编程之一:选择合适的多线程模型
本篇概述:
@FCL4.0中已经存在的线程模型,以及它们之间异同点;
@多线程编程模型的选择。
1:异步、多线程、任务、并行的本质
这四个概念对应在CLR中的本质,本质都是多线程。
异步,简单的讲就是BeginInvoke、EndInvoke模式,它在CLR内部线程池进行管理;
多线程,体现在C#中,可以由类型Thread发起。也可以由ThreadPool发起。前者不受CLR线程池管理,后者则是。FCL团队为了各种编程模型的方便,还另外提供了BackgroundWorker和若干个Timer,基本上它们都是ThreadPool的加强,增加了一些和调用者线程的交互功能;
任务(Task),为FCL4.0新增的功能,在一个称之为任务并行库(TPL)的地方,其实也就是System.Threading.Tasks命名空间下。任务并行库名字取的很玄乎,其实它也是CLR线程池的加强。优化了线程间的调度算法,增加了和调用者线程的交互功能;
并行(Parallel),为FCL4.0新增的功能,也属于TPL。并行在后台使用Task进行管理,说白了,因为Task使用的线程池线程,所以Parallel自然使用的也是线程池线程进行管理,它的本质仅仅是进一步简化了Task。在这里要增进一个对于并行的理解。实际上,多线程天然就是并行的。及时不用任务并行库,用Thread类型新起两个线程,CLR或者说Windows系统也会将这两个线程根据需要安排到两个CPU上去执行。所以,并不是因为多了任务并行库,CLR才支持并行计算,任务并行库只是提供了一组API,使我们能够更好的操纵线程进行并行开发而已。
2:遗憾
Jeffrey Richter大叔说,微软提供了这么多线程模型,是遗憾的,因为这制造了混乱。很多开发者都不知道该选用哪个类型来编写自己的多线程代码。我们对微软总是又爱又恨,它总是不停的更新一些东西,逼迫我们不停的学习。但是也好,进步导致它不会过早死掉,让我们彻底失掉饭碗。
C#刚出来的被人笑,现在它的很多语法特性已经比Java优美。很多时候我们太擅长于嘲笑,以致最后只能哭。顺便说一句,我依然是那么的喜欢JAVA,只是很久没用它而已。
3:现在,该用什么来编写多线程
如果你在FRAMEWORK4.0下编写代码,那么应该按照这个优先级来撰写多线程代码:
优先 | 次优先 | 不得以 |
Parallel(含扩展库PLinq) Task | ThreadPool(BackgroundWorker,Timer) 异步 | Thread |
这个表满足了大部分情况下的一个优先级指导,但在某些情况下会有例外。
3.1:为什么 Parallel和Task优先级一样,而不是优于Task?
Parallel虽然在后台使用Task进行管理,并且它所谓简化了对于Task的操作,但是它有一个重要的特征区别与Task:Parallel会阻滞调用者线程。查看Paralle的成员,有For、ForEach、Invoke方法,它甚至都没提供一个BeginInvoke方法,也很好的暗示了这一点。不过虽然是同步的执行的,Parallel还是会把多个任务分配到多个CPU上去。
Task被用的最多的是Start方法,它不会阻滞主线程。虽然Task也提供了同步的启动线程的方法RunSynchronously,但一般用的不多。
3.2:何时用异步,何时用线程或线程池
这需要从“IO操作的DMA(Direct Memory Access)模式”讲起。通过DMA的数据交换几乎可以不损耗CPU的资源。在硬件部分,硬盘、网卡、声卡、显卡等都有DMA功能。可以简单的认为,当我们的工作线程需要操作I/O资源的时候(如读取一个大文件、读取一个网页、读取Socke包等),我们就需要用异步去做这些事情。异步模式只会在工作开始以及工作结束的时候占用CLR线程池,其它时候由硬盘、网卡等硬件设备来处理具体的工作,这就不会过多占用到CPU空间和时间损耗。
概括而言:
计算密集型工作,直接采用线程;
IO密集型工作,采用异步机制;
当我们不清楚什么工作是I/O密集型的,一个不是很恰当的指导就是:查看FCL类型成员,如果成员提供了类似BeginDosomething方法的,则优先使用它,而不是新起一个线程或丢到线程池。
3.3:线程池的优势
新起线程,会带来很大的开销,这些开销主要集中在:分配线程内核对象、线程环境块、用户模式栈、内核模式栈所需要的内存空间,加载的DLL的DLLMain方法,并传递连接标志,以及线程上下文切换。由于线程如此昂贵,所以对于普通的开发要求来说,线程池就是一个很好的选择。线程池替开发人员管理工作线程,当一项工作完毕的时候,CLR不会销毁这个线程,而是会保留这个线程一段时间,看是否有别的工作需要这个线程。至于何时销毁或新起线程,由CLR决定。
3.4:何时用Thread
以上的各种线程模型,它们最终都是Thread。 那么什么时候需要Thread直接出场呢?
最重要的使用Thread的理由是,我们需要控制线程的优先级。Thread之上的线程模型都不支持优先级设置。设置一个线程的高优先级可以使它获得更多的CPU时间;
再者,可以控制线程为前台线程。当然,由Thread新起的线程默认就是前台线程。前台线程不随着调用者线程的中断而中断,这使得我们可以用Thread来进行一些关键性的操作。
猜你喜欢
- 本文实例讲述了Java实现的计时器【秒表】功能。分享给大家供大家参考,具体如下:应用名称:Java计时器用到的知识:Java GUI编程开发
- SpringMVC4使用数据校验的时候需要使用hibernate-validator的包第一步添加依赖 <d
- 目录环境准备1.数据库操作1.1获取所有数据库1.2获取指定库的所有集合名1.3.删除数据库2.文档操作2.1插入文档2.2查询文档2.3分
- Quartz简介Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中。它提供了巨大的
- 简单回顾一下CAS算法CAS算法 即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多
- 前言Docker旨在提供一种应用程序的自动化部署解决方案,在 Linux 系统上迅速创建一个容器(轻量级虚拟机)并部署和运行应用程序,并通过
- SpringBoot2底层注解一、@ImportResource@Conditional注解,是根据条件进行装配。满足了 Condition
- MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。如果你有使用 JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL
- 三目条件运算公式为 x?y:z 其中x的运算结果为boolean类型,先计算x的值,若为true,则整个三目运算的结果为表达式y
- Java语言的历程丰富多彩,被现在众多程序员和企业广泛使用,不用质疑这是Java的领先技术的结果。Java是Sun公司开发的一种编程语言,S
- 本文实例为大家分享了Java NIO实战之多人聊天室的具体代码,供大家参考,具体内容如下NIO服务端public class NioServ
- 一、Java把这些不同来源和目标的数据都统一抽象为数据流。Java语言的输入输出功能是十分强大而灵活的。在Java类库中,IO部分的内容是很
- 从Java 5开始 引入了 JConsole。JConsole 是一个内置 Java 性能分析器,可以从命令行或在 GUI shell 中运
- Java把内存分成两种,一种叫做栈内存,一种叫做堆内存在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。
- cpu是时分(time division)的,操作系统里有很多线程,每个线程的运行时间由cpu决定,cpu会分给每个线程一个时间片,时间片是
- 场景我们团队现在面临着多端数据接口对接的问题,为了解决这个问题我们定义了接口对接的规范,前端(安卓,Ios,web前端)和后端进行了数据的格
- 定义里氏替换原则(Liskov Substitution Principle,LSP),官方定义如下: 如果对每一个类型为S的对象o1,都有
- 1、深度总结引用一位网友的话,说的非常好,如果别人问你static的作用;如果你说静态修饰 类的属性 和 类的方法 别人认为你是合格的;如果
- spring boot 使用profile来分区配置很多时候,我们项目在开发环境和生成环境的环境配置是不一样的,例如,数据库配置,在开发的时
- 本文实例为大家分享了java文件处理工具类的具体代码,供大家参考,具体内容如下import java.io.BufferedInputStr