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,GetMinThreadsGetAvailableThreads。这里建议你,不要调用上述任何方法,限制线程池的线程数,一般只会造成应用程序的性能变得更差,而不会变得更好

  如果你认为自己的应用程序需要几百个或者几千个线程,那只表明,你的应用程序的架构和使用线程的方式已出现严重的问题。

现在来看看如何管理工作者线程,之前需要来看看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完结篇)的更多相关文章

  1. C# 线程(二):关于线程的相关概念

    From : http://kb.cnblogs.com/page/42528/ 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源. 而一个进程又 ...

  2. 【Linux 线程】同一个进程中的线程共享哪些资源

    进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线 ...

  3. C# 多线程的自动管理(线程池) 基于Task的方式

    C# 多线程的自动管理(线程池) 在多线程的程序中,经常会出现两种情况:    1. 应用程序中线程把大部分的时间花费在等待状态,等待某个事件发生,然后给予响应.这一般使用 ThreadPool(线程 ...

  4. 使用ExecutorCompletionService 管理线程池处理任务的返回结果

    在我们日常使用线程池的时候,经常会有需要获得线程处理结果的时候.此时我们通常有两种做法. 1. 使用并发容器将callable.call() 的返回Future存储起来.然后使用一个消费者线程去遍历这 ...

  5. ExecutorService实际上是一个线程池的管理工具

    在Java5之后,并发线程这块发生了根本的变化,最重要的莫过于新的启动.调度.管理线程的一大堆API了.在Java5以后,通过Executor来启动线程比用 Thread的start()更好.在新特征 ...

  6. C#多线程学习(四) 多线程的自动管理(线程池)

    在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应                   这一般使用ThreadPo ...

  7. Python之路(第四十六篇)多种方法实现python线程池(threadpool模块\multiprocessing.dummy模块\concurrent.futures模块)

    一.线程池 很久(python2.6)之前python没有官方的线程池模块,只有第三方的threadpool模块, 之后再python2.6加入了multiprocessing.dummy 作为可以使 ...

  8. 线程池的管理类MyThreadPoolManager

    import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executor; import java.ut ...

  9. 如何应用CLR线程池来管理多线程

        class Program     {         static void Main(string[] args)         {             int intWorkerT ...

随机推荐

  1. 慢吞吞的pip切换源

    http://blog.csdn.net/gz_liuyun/article/details/52778198

  2. Notes of Daily Scrum Meeting(11.4)

    Notes of Daily Scrum Meeting 2014年11月4日  星期二  20:30—21:00 团队成员 今日团队任务 当日工作分配额 完成情况 陈少杰 阅读理解代码中底层与数据库 ...

  3. Daily Scrum 11.15

    今日完成任务: 1.在回答页面显示用户的相关信息 2.重写了搜索方法,并在自己的Demo网站测试成功 3.修改问题实体属性,加入悬赏积分:并在问题列表页面显示问题悬赏分数 遇到困难:一个是对于学长的搜 ...

  4. H-ui框架制作选项卡

    本案例运用H-ui框架,写了一个选项卡案例 1. html代码(固定这样写,用一个div包裹控制条tabBar和内容条tabCon) <div id="tab-index-carteg ...

  5. 1~n中1的和

    题目:给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数: 要求:写一个函数f(n),返回1到n之间出现“1“的个数, 思路: 1.先判断这个数共多少位,假设为n位: ...

  6. 处理Git不能上传大于100M文件问题

    记录一下自己工作遇到的问题,免得下次再遇到了还到处网上查资料解决. 自己的项目的版本控制用的是Git,代码仓库在github托管.项目里用到了百度导航SDK,由于百度导航SDK有了新版本,于是就更新到 ...

  7. grunt入门讲解7:项目脚手架grunt-init

    grunt-init是一个用于自动创建项目脚手架的工具.它会基于当前工作环境和你给出的一些配置选项构建一个完整的目录结构.至于其所生成的具体文件和内容,依赖于你所选择的模版和构建过程中你对具体信息所给 ...

  8. IDEA小插件之快速修改Maven多模块的工程版本

    Github:https://github.com/zwjlpeng/versions 问题在Maven构建的多模块块程中,如果我们需要修改工程的版本号,会怎么操作呢example例如工程A包括了A- ...

  9. 0506Scrum项目1.0

    1.确定选题. 应用NABCD模型,分析你们初步选定的项目,充分说明你们选题的理由. 录制为演说视频,上传到视频网站,并把链接发到团队博客上. 截止日期:2016.5.6日晚10点 团队名称:虫洞 团 ...

  10. vue 组件 模板input双向数据数据

    <!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>T ...