许多应用程序使用多个线程,但这些线程常常在休眠状态中耗费大量的时间来等待事件发生。其余线程可能进入休眠状态,而且仅按期被唤醒以轮询更改或更新状态信息,而后再次进入休眠状态。为了简化对这些线程的管理,.NET框架为每个进程提供了一个线程池,使应用程序可以根据须要来有效地利用多个线程。一个线程监视排到线程池的若干个等待操做的状态。当一个等待操做完成时,线程池中的一个辅助线程就会执行对应的回调函数。线程池中的线程由系统进行管理,程序员不须要费力于线程管理,能够集中精力处理应用程序任务。程序员
线程池是一种多线程处理形式,处理过程当中将任务添加到队列,而后在建立线程后自动启动这些任务。线程池线程都是后台线程。每一个线程都使用默认堆栈大小,以默认的优先级运行,并处于多线程单元中。若是某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另外一个辅助线程来使全部处理器保持繁忙。若是全部线程池线程都始终保持繁忙,但队列中包含挂起的工做,则线程池将在一段时间以后建立另外一个辅助线程。但线程的数目永远不会超过最大值。超过最大值的其余线程能够排队,但它们要等到其余线程完成后才启动。多线程
线程池特别适合于执行一些须要多个线程的任务。使用线程池可以优化这些任务的执行过程,从而提升吞吐量,它不只可以使系统针对此进程优化该执行过程,并且还可以使系统针对计算机上的其余进程优化该执行过程。若是须要启动多个不一样的任务,而不想分别设置每一个线程的属性,则可使用线程池。app
若是应用程序须要对线程进行特定的控制,则不适合使用线程池,须要建立并管理本身的线程。框架
在如下几种状况下,适合于使用线程池线程:异步
(1)不须要前台执行的线程。函数
(2)不须要在使用线程具备特定的优先级。性能
(3)线程的执行时间不易过长,不然会使线程阻塞。因为线程池具备最大线程数限制,所以大量阻塞的线程池线程可能会阻止任务启动。优化
(4)不须要将线程放入单线程单元。全部 ThreadPool 线程均不处于多线程单元中。spa
(5)不须要具备与线程关联的稳定标识,或使某一线程专用于某一任务。操作系统
在多线程的程序中,常常会出现两种状况:一种是在应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,而后才能给予响应,这通常使用ThreadPool(线程池)来解决;另外一种状况是在线程平时都处于休眠状态,只是周期性地被唤醒,这通常使用Timer(定时器)来解决。下面对ThreadPool类进行详细说明。
ThreadPool类提供一个线程池,该线程池可用于发送工做项、处理异步 I/O、表明其余线程等待以及处理计时器。该类提供一个由系统维护的线程池(能够看做一个线程的容器),该容器须要Windows 2000以上系统支持,由于其中某些方法调用了只有高版本的Windows才有的API函数。
下面介绍一下该类所提供的方法,如表1所示。
表1 ThreadPool类的方法
方法 |
描述 |
BindHandle |
将操做系统句柄绑定到ThreadPool |
GetAvailableThreads |
检索由GetMaxThreads方法返回的最大线程池线程数和当前活动线程数之间的差值 |
GetMaxThreads |
检索能够同时处于活动状态的线程池请求的数目。全部大于此数目的请求将保持排队状态,直到线程池线程变为可用 |
GetMinThreads |
检索线程池在新请求预测中维护的空闲线程数 |
QueueUserWorkItem |
将方法排入队列以便执行。此方法在有线程池线程变得可用时执行 |
RegisterWaitForSingleObject |
注册正在等待WaitHandle的委托 |
SetMaxThreads |
设置能够同时处于活动状态的线程池的请求数目。全部大于此数目的请求将保持排队状态,直到线程池线程变为可用 |
SetMinThreads |
设置线程池在新请求预测中维护的空闲线程数 |
UnsafeQueueNativeOverlapped |
将重叠的 I/O 操做排队以便执行 |
UnsafeQueueUserWorkItem |
注册一个等待 WaitHandle 的委托 |
UnsafeRegisterWaitForSingleObject |
将指定的委托排队到线程池 |
经过以上方法,能够对线程池进行设置以及相应的操做,那么,咱们何时使用线程池呢?咱们在用Thread类调用线程时,一次只能使用一个线程来建立和删除线程,这种方式的创建和删除线程对CPU的使用是很频繁的,为了节省CPU的负荷,可使用线程池对线程进行操做,为了使读者更深刻的了解Thread类与ThreadPool类的差异。
在如下状况下,应使用ThreadPool类:
ThreadPool类会在线程的托管池中重用已有的线程。使用完线程后,线程就会返回线程池,供之后使用。ThreadPool有25个可用的线程(每一个处理器)。
在使用线程池时,通常调用ThreadPool类的QueueUserWorkItem方法。
用户并不须要自已创建线程,只须要把要进行的操做写成函数,而后做为参数传递给ThreadPool类的QueueUserWorkItem方法就好了,传递的方法是依靠WaitCallback代理对象,而线程的创建、管理、运行等工做都是由系统自动完成的,用户无须考虑那些复杂的细节问题。ThreadPool类的用法:首先程序建立了一个ManualResetEvent对象,该对象就像一个信号灯,能够利用它的信号来通知其它线程。
为了读者能更好的控制线程池,下面讲解一下如何设置和读取线程池的最大线程数和最小空闲线程数。
1.线程池的最大线程数
可排队到线程池的操做数仅受可用内存的限制;可是,线程池限制进程中能够同时处于活动状态的线程数。默认状况下,限制每一个CPU可使用25个辅助线程和1000 个I/O完成线程。
经过使用GetMaxThreads和SetMaxThreads方法能够控制最大线程数。下面对这两个方法的声明进行一下说明。
(1)GetMaxThreads方法
该方法检索能够同时处于活动状态的线程池请求的数目。全部大于此数目的请求将保持排队状态,直到线程池线程变为可用。其语法以下:
public static void GetMaxThreads(out int workerThreads,out int completionPortThreads)
参数说明:
workerThreads:Int32,线程池中辅助线程的最大数目。
CompletionPortThreads:Int32,线程池中异步I/O线程的最大数目。
(2)SetMaxThreads方法
该方法设置能够同时处于活动状态的线程池的请求数目。全部大于此数目的请求将保持排队状态,直到线程池线程变为可用。
[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)] public static bool SetMaxThreads(int workerThreads,int completionPortThreads)
参数说明:
workerThreads:Int32,线程池中辅助线程的最大数目。
CompletionPortThreads:Int32,线程池中异步 I/O 线程的最大数目。
返回值:bool型,若是更改为功,则为true;不然为false。
下面经过一段代码对GetMaxThreads和SetMaxThreads方法的应用进行一下讲解,主要是显示线程池中辅助线程的最大数和线程池中异步I/O线程的最大数,以及用户自行设置后的线程池状况。代码以下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { //设置正在等待线程的事件为终止 AutoResetEvent autoEvent = new AutoResetEvent(false); int workerThreads; int portThreads; //获取处于活动状态的线程池请求的数目 ThreadPool.GetMaxThreads(out workerThreads, out portThreads); //在控制台中显示处于活动状态的线程池请求的数目 Console.WriteLine("设置前,线程池中辅助线程的最大数为:" + workerThreads.ToString() + ";线程池中异步I/O线程的最大数为:" + portThreads.ToString()); workerThreads = 10;//设置辅助线程的最大数 portThreads = 500;//设置线程池中异步I/O线程的最大数 //设置处于活动状态的线程池请求的数目 ThreadPool.SetMaxThreads(workerThreads, portThreads); ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadMethod), autoEvent);//执行线程池 //在控制台中显示设置后的处于活动状态的线程池请求的数目 Console.WriteLine("设置后,线程池中辅助线程的最大数为:" + workerThreads.ToString() + ";线程池中异步I/O线程的最大数为:" + portThreads.ToString()); Console.ReadLine(); } static void ThreadMethod(object stateInfo) { Console.WriteLine("执行线程池"); } } }
运行结果以下:
注意:在 .NET Framework 1.0 和 1.1 版中,不能从托管代码中设置线程池大小。承载公共语言运行库的代码可使用 mscoree.h 中定义的 CorSetMaxThreads 设置该大小。
2.线程池的最小空闲线程数
即便是在全部线程都处于空闲状态时,线程池也会维持最小的可用线程数,以便队列任务能够当即启动。将终止超过此最小数目的空闲线程,以节省系统资源。默认状况下,每一个处理器维持一个空闲线程。
在启动新的空闲线程以前,线程池具备一个内置延迟(在 .NET Framework 2.0 版中为半秒钟)。应用程序在短时间内按期启动许多任务时,空闲线程数的微小增长会致使吞吐量显著增长。将空闲线程数设置得太高会浪费系统资源。
使用GetMinThreads和SetMinThreads方法能够控制线程池所维持的空闲线程数。下面对这两个方法进行一下说明。
(1)GetMinThreads方法
该方法用于检索线程池在新请求预测中维护的空闲线程数。其语法以下:
public static void GetMinThreads(out int workerThreads,out int completionPortThreads)
参数说明:
workerThreads:Int32,当前由线程池维护的空闲辅助线程的最小数目。
CompletionPortThreads:Int32,当前由线程池维护的空闲异步I/O线程的最小数目。
(2)SetMinThreads方法
该方法用于设置线程池在新请求预测中维护的空闲线程数。其语法以下:
[SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)] public static bool SetMinThreads(int workerThreads,int completionPortThreads)
参数说明:
workerThreads:类型:Int32,要由线程池维护的新的最小空闲辅助线程数。
CompletionPortThreads:Int32,要由线程池维护的新的最小空闲异步 I/O 线程数。
返回值:bool型,若是更改为功,则为 true;不然为 false。
下面用一段代码来详细说明一下GetMinThreads和SetMinThreads方法的应用,代码以下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { int minWorker, minIOC; //获取线程池在新请求预测中维护的默认空闲线程数 ThreadPool.GetMinThreads(out minWorker, out minIOC); Console.WriteLine("设置前,线程池维护的空闲辅助线程的最小数目为:" + minWorker.ToString() + ";线程池维护的空闲异步I/O线程的最小数目为:" + minIOC.ToString());//在控制台中显示线程池的默认空闲线程数 minWorker = 4;//设置线程池维护的空闲辅助线程的最小数 minIOC = 10;//设置线程池维护的空闲异步I/O线程的最小数 if (ThreadPool.SetMinThreads(minWorker, minIOC))//若是更改为功 { Console.WriteLine("设置后,线程池维护的空闲辅助线程的最小数目为:" + minWorker.ToString() + ";线程池维护的空闲异步I/O线程的最小数目为:" + minIOC.ToString());//在控制台中显示更改后的线程池的默认空闲线程数 } else { Console.WriteLine("没有设置"); } Console.ReadLine(); } } }
运行结果以下:
注意:在.NET Framework 1.0版中,不能设置最小空闲线程数。