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完结篇)的更多相关文章

  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. Tomcat之初识初体验

    1.what's this? Stable performance, free Java web application server! 相关: Java,Javac,JVM,JRE,JDK,Java ...

  2. [linux] LVM磁盘管理(针对xfs和ext4不同文件系统)

    简单来说就是:PV:是物理的磁盘分区VG:LVM中的物理的磁盘分区,也就是PV,必须加入VG,可以将VG理解为一个仓库或者是几个大的硬盘LV:也就是从VG中划分的逻辑分区如下图所示PV.VG.LV三者 ...

  3. AJAX(Asynchronous JavaScript and XML)学习笔记

    基本概念: 1.AJAX不是一种新的编程语言,而是一种使用现有标准的新方法. 2.AJAX最大的优点是在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容,用于创建快速动态网页(传统网页如 ...

  4. (第十一周)Beta—review阶段成员贡献分

    项目名:食物链教学工具 组名:奋斗吧兄弟 组长:黄兴 组员:李俞寰.杜桥.栾骄阳.王东涵 个人贡献分=基础分+表现分 基础分=5*5*0.5/5=2.5 成员得分如下: 成员 基础分 表现分 个人贡献 ...

  5. 团队编程--MP3播放器

    设计思路: 这次的作业是一个MP3播放器,它是一个团队项目.由于我们都没接触过这类的编程.刚开始的时候我们是不知道从什么地方着手的.经过我们的商量我们决定从现在市场主流的音乐播放器上找到几个主要的功能 ...

  6. linux下使用pip在虚拟环境下安装tensorflow-gpu

    1. 查看已安装包库 pip list 2. linux下下载虚拟环境 sudo apt-get install python-virtualenv 3.建立全新的virtualenv环境 virtu ...

  7. JAVA异常架构图及常见面试题

    红色为检查异常,就是eclipse要提示你是try catch 还是throws. 非检查异常,就是/0,nullpointexception,数据越界访问indexOfOutBounds 异常 错误 ...

  8. 《软件工程和Python》第0周作业1

    写在前面的话 欢迎大家开始一段新的课程学习!从开博客开始吧.每次博客作业都会有评分,计入总成绩哦. 1.   截止日期 本次作业的提交截止时间:见老师要求 2.   作业要求 (1)建立个人技术博客和 ...

  9. CANOpen学习指南

    对于初学者,相对于其他总线的资料来说,在国内CANOpen的资料并不多.而且并不是所有资料都适合初学者看的.这里给出一些建议,对CANOpen感兴趣的,可以参考一下学习的顺序. 前提:需要对CAN总线 ...

  10. 作死实验,删除libc.so.6

    参考https://www.cnblogs.com/fjping0606/p/4551475.html https://www.cnblogs.com/weijing24/p/5890031.html ...