C#线程篇---线程池如何管理线程(6完结篇)
C#线程基础在前几篇博文中都介绍了,现在最后来挖掘一下线程池的管理机制,也算为这个线程基础做个完结。
我们现在都知道了,线程池线程分为工作者线程和I/O线程,他们是怎么管理的?
对于Microsoft设计的CLR线程池,线程池会随着CLR的每个版本的发布,都会发生变化,很难去挖掘,这里的提议是:
最好将线程看成一个黑盒。不要拿单个应用程序去衡量这个黑盒的性能,因为它对任何一个应用程序来说都无法做到完美。
相反,它是一种常规用途的线程调度技术,面向大量应用程序;它对某些应用程序的效果要好于其他应用程序。
目前,它的工作情况非常理想,这里建议你信任它,因为你很难高出一个比CLR自带的那个更好的线程池。另外,随着时间的推移,线程池代码内部,会更改它管理线程的方式,所以大多数应用程序的性能会变得越来越好。
CLR允许开发人员设置线程池创建最大线程数。然后有些开发人员感觉好像有必要对线程池拥有的线程数量进行限制,因为有些人觉得,要合理利用资源,做到自己调配资源,是很有成就感的事(是不是强迫症?)
但实践证明,线程池永远都不应该为池中的线程数设置上限,因为可能发生饥饿或死锁。
为什么这么说?
假如队列中有1000个工作项,但这些工作项全都因为一个事件而阻塞(多么可怕的事),等到第1001个工作项发出信号才能解除阻塞。如果设置最大1000个线程,第1001个线程就不会执行,所以1000个线程会一直阻塞,然后你能想到的,用户被迫终止应用程序,并丢失他们的所有未保存的工作。你不能让线程阻塞!
由于存在饥饿和死锁问题,所以CLR团队一直都在稳步的增加线程池默认能拥有的最大线程数。
目前默认值是最大1000个。这可以看成是不限数量,为什么?
一个32位进程最大的2GB的可用地址空间,加载了一组Win32和CLR DLLs,并分配了本地堆和托管堆之后,剩余约1.5GB的地址空间。由于每个线程都要为用户模式栈和线程环境块准备超过1MB的内存,所以在一个32位的进程中,最多能有1360个线程。试图创建更多线程,则会抛出OutMemoryException。
一个64位进程提供了8TB的地址空间,所以理论上可以创建千百万个线程。但是分配这么多线程,纯属浪费,尤其是当理想线程数等于机器的CPU数的时候。
ThreadPool类提供了几个静态方法,调用它们可以设置和查询线程池的线程数:GetMaxThreads,SetMaxThreads,GetMinThreads和GetAvailableThreads。这里建议你,不要调用上述任何方法,限制线程池的线程数,一般只会造成应用程序的性能变得更差,而不会变得更好。
如果你认为自己的应用程序需要几百个或者几千个线程,那只表明,你的应用程序的架构和使用线程的方式已出现严重的问题。
现在来看看如何管理工作者线程,之前需要来看看CLR线程池是什么样的:

这是工作者线程的数据结构。ThreadPool.QueueUserWorkItem方法和Timer类总是会将工作项放到全局队列中。
而工作线程采用一个先入先出(FIFO)算法将工作项从这个队列取出,并处理它们。(学过数据结构的应该知道FIFO)
由于多个工作者线程可能同时从全局队列中拿走工作项,所以所有工作者线程都竞争一个线程同步锁,以保证两个或多个线程不会获取同一个工作项。同步锁在某些应用程序总可能对伸缩性和性能造成某种程度的限制。
当一个非工作者线程调度一个Task时,Task会添加到全局队列。但是,每个工作者线程都有它自己的本地队列,上图可以看到,工作者线程是主,对应的本地队列是附,当一个工作者线程调度一个Task时,Task会添加到调用线程的本地队列,而不是全局队列。
现在来看下工作者线程的描述:
工作者线程之所以称为Workers,它是名副其实的。它就是一“工作狂”,打个比方:
工作狂是什么?做完自己的事还不够,还要去抢别人的事做,别人的事做完了,就去找公共的事做,除非没有事干,要不然不会停下。
用这个比方,下面我的介绍就会浅显很多了。
一个工作者线程准备处理一个工作项时,它总是先检查它的本地队列来查找一个Task。如果存在Task,工作者线程就从它的本地队列中移除Task,并对工作项进行处理。
要注意的是,工作者线程是采用一个“栈”式结构,也就是后入先出(LIFO)算法,将任务从它的本队队列中取出。由于工作者线程是唯一允许访问自己的本地队列头的线程,所以不需要同步锁,而且在队列中添加和删除任务的速度非常快,这个行为的副作用就是,它的执行顺序是相反的,后入的先执行。
还有哦,如果一个工作者线程发现本地队列变空了,那么它就会尝试从另一个工作者线程的本地队列中“偷”一个Task,并获取一个线程同步锁,不过这种情况还是很少发生的。
再是,当所有本地队列都为空了,工作者线程就使用FIFO算法,从全局队列中提取一个工作项,当然也会取得它的锁。
现在所有队列都为空了,工作者线程就会自己进入睡眠状态,等待事情的发生。如果睡眠了时间太长,它会自己醒来,并销毁自身。
线程池会快速创建工作者线程,工作者线程的数量等于ThreadPool的SetMinThreads方法的值(默认是你的电脑CPU数),32位进程最多用32个CPU,64位进程最多可用64个CPU。然后创建工作者线程达到机器CPU数时,线程池会监视工作项的完成速度,如果工作项完成的时间太长,线程池就会创建更多的工作者线程,使工作加速完成。如果工作项的完成速度开始变快了,工作者线程就会被销毁。
线程池的设计是很人性话的,有没有体会到?
线程基础用了这么久才介绍完,新的起点又来啦。^_^
C#线程篇---线程池如何管理线程(6完结篇)的更多相关文章
- C# 线程(二):关于线程的相关概念
From : http://kb.cnblogs.com/page/42528/ 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源. 而一个进程又 ...
- 【Linux 线程】同一个进程中的线程共享哪些资源
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线 ...
- C# 多线程的自动管理(线程池) 基于Task的方式
C# 多线程的自动管理(线程池) 在多线程的程序中,经常会出现两种情况: 1. 应用程序中线程把大部分的时间花费在等待状态,等待某个事件发生,然后给予响应.这一般使用 ThreadPool(线程 ...
- 使用ExecutorCompletionService 管理线程池处理任务的返回结果
在我们日常使用线程池的时候,经常会有需要获得线程处理结果的时候.此时我们通常有两种做法. 1. 使用并发容器将callable.call() 的返回Future存储起来.然后使用一个消费者线程去遍历这 ...
- ExecutorService实际上是一个线程池的管理工具
在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动.调度.管理线程的一大堆API了.在Java5以后,通过Executor来启动线程比用 Thread的start()更好.在新特征 ...
- C#多线程学习(四) 多线程的自动管理(线程池)
在多线程的程序中,经常会出现两种情况: 一种情况: 应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应 这一般使用ThreadPo ...
- Python之路(第四十六篇)多种方法实现python线程池(threadpool模块\multiprocessing.dummy模块\concurrent.futures模块)
一.线程池 很久(python2.6)之前python没有官方的线程池模块,只有第三方的threadpool模块, 之后再python2.6加入了multiprocessing.dummy 作为可以使 ...
- 线程池的管理类MyThreadPoolManager
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executor; import java.ut ...
- 如何应用CLR线程池来管理多线程
class Program { static void Main(string[] args) { int intWorkerT ...
随机推荐
- Windows10子系统安装ubuntu+kali渗透环境
Windows10安装子系统ubuntu,安装完ubuntu后再安装katoolin才能使用kali. (katoolin渗透测试的Linux发行版,它可以让你在其他Linux发行版上使用Kali的全 ...
- hive对于lzo文件处理异常Caused by: java.io.IOException: Compressed length 842086665 exceeds max block size 67108864 (probably corrupt file)
hive查询lzo数据格式文件的表时,抛 Caused by: java.io.IOException: Compressed length 842086665 exceeds max block s ...
- Django_rest_framework_Serializer
序列化Serializer 序列化用于对用户请求数据进行验证和数据进行序列化(为了解决queryset序列化问题). 那什么是序列化呢?序列化就是把对象转换成字符串,反序列化就是把字符串转换成对象 m ...
- js/jquery去掉空格,回车,换行示例代码
Jquery: $("#accuracy").val($("#accuracy").val().replace(/\ +/g,""));// ...
- Scrum Meeting 10.25
成员 已完成任务 下一阶段任务 用时 徐越 阅读前端代码中和通信相关的部分 学习服务器配置 4h 赵庶宏 阅读前端代码中和通信相关的部分 学习服务器配置 4h 薄霖 继续做UI开发 界面优化 4h 武 ...
- Scrum Meeting 10.22
Scrum Meeting No.2 今天的主要任务是配置安卓开发环境,并运行上一届的项目. 主流的安卓开发环境有eclipse+ADT+SDK和android studio两种.两种环境的文件架构似 ...
- 炸弹人——NABCD分析
炸弹人——NABCD分析结果 N:需求:本软件应用于学生,学生可以在课余时间放松心情,缓解学习压力. A:做法:使用Cocosdx和Visual Studio 2010结合,之间用Python使其结合 ...
- 优化Hibernate所鼓励的7大措施:
优化Hibernate所鼓励的7大措施: 1.尽量使用many-to-one,避免使用单项one-to-many 2.灵活使用单向one-to-many 3.不用一对一,使用多对一代替一对一 4.配置 ...
- C#窗体——四则运算
用户需求:程序能接收用户输入的整数答案,并判断对错程序结束时,统计出答对.答错的题目数量.补充说明:0——10的整数是随机生成的用户可以选择四则运算中的一种用户可以结束程序的运行,并显示统计结果.在此 ...
- Docker的volume机制实现容器数据的持久性存储
1:可以启动一个容器的时候,临时指定挂载的volume,这个volume会自动创建,无需在宿主机上事先创建 docker run -it -v /busybox busybox:latest 登入到b ...