C#中的线程池使用(一)
1 线程池的概念
许多应用程序使用多个线程,但这些线程经常在休眠状态中耗费大量的时间来等待事件发生。其他线程可能进入休眠状态,并且仅定期被唤醒以轮询更改或更新状态信息,然后再次进入休眠状态。为了简化对这些线程的管理,.NET框架为每一个进程提供了一个线程池,使应用程序能够根据需要来有效地利用多个线程。一个线程监视排到线程池的若干个等待操作的状态。当一个等待操作完成时,线程池中的一个辅助线程就会执行对应的回调函数。线程池中的线程由系统进行管理,程序员不需要费力于线程管理,可以集中精力处理应用程序任务。
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间之后创建另一个辅助线程。但线程的数目永远不会超过最大值。超过最大值的其他线程可以排队,但它们要等到其他线程完成后才启动。
2 线程池的应用范围
线程池特别适合于执行一些需要多个线程的任务。使用线程池能够优化这些任务的执行过程,从而提高吞吐量,它不仅能够使系统针对此进程优化该执行过程,而且还能够使系统针对计算机上的其他进程优化该执行过程。如果需要启动多个不同的任务,而不想分别设置每个线程的属性,则可以使用线程池。
如果应用程序需要对线程进行特定的控制,则不适合使用线程池,需要创建并管理自己的线程。
在以下几种情况下,适合于使用线程池线程:
(1)不需要前台执行的线程。
(2)不需要在使用线程具有特定的优先级。
(3)线程的执行时间不易过长,否则会使线程阻塞。由于线程池具有最大线程数限制,因此大量阻塞的线程池线程可能会阻止任务启动。
(4)不需要将线程放入单线程单元。所有 ThreadPool 线程均不处于多线程单元中。
(5)不需要具有与线程关联的稳定标识,或使某一线程专用于某一任务。
3 ThreadPool类
在多线程的程序中,经常会出现两种情况:一种是在应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应,这一般使用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类:
- 要以最简单的方式创建和删除线程;
- 应用程序使用线程的性能要优先考虑。
- 在以下情况下,应使用Thread类:
- 要控制所创建线程的优先级;
- 希望所使用的线程维护其标识,该标识要与线程一起进行各种操作,经过许多不同的时间段;
- 所使用的线程的寿命较长。
ThreadPool类会在线程的托管池中重用已有的线程。使用完线程后,线程就会返回线程池,供以后使用。ThreadPool有25个可用的线程(每个处理器)。
在使用线程池时,一般调用ThreadPool类的QueueUserWorkItem方法。
用户并不需要自已建立线程,只需要把要进行的操作写成函数,然后作为参数传递给ThreadPool类的QueueUserWorkItem方法就行了,传递的方法是依靠WaitCallback代理对象,而线程的建立、管理、运行等工作都是由系统自动完成的,用户无须考虑那些复杂的细节问题。ThreadPool类的用法:首先程序创建了一个ManualResetEvent对象,该对象就像一个信号灯,可以利用它的信号来通知其它线程。
4 线程池的设置
为了读者能更好的控制线程池,下面讲解一下如何设置和读取线程池的最大线程数和最小空闲线程数。
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 = ;//设置辅助线程的最大数
portThreads = ;//设置线程池中异步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 = ;//设置线程池维护的空闲辅助线程的最小数
minIOC = ;//设置线程池维护的空闲异步I/O线程的最小数
if (ThreadPool.SetMinThreads(minWorker, minIOC))//如果更改成功
{
Console.WriteLine("设置后,线程池维护的空闲辅助线程的最小数目为:" + minWorker.ToString() + ";线程池维护的空闲异步I/O线程的最小数目为:" + minIOC.ToString());//在控制台中显示更改后的线程池的默认空闲线程数
}
else
{
Console.WriteLine("没有设置");
}
Console.ReadLine();
}
}
}
运行结果如下:

注意:在.NET Framework 1.0版中,不能设置最小空闲线程数。
C#中的线程池使用(一)的更多相关文章
- android中的线程池学习笔记
阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...
- Java5中的线程池实例讲解
Java5增加了新的类库并发集java.util.concurrent,该类库为并发程序提供了丰富的API多线程编程在Java 5中更加容易,灵活.本文通过一个网络服务器模型,来实践Java5的多线程 ...
- Spring中的线程池和定时任务功能
1.功能介绍 Spring框架提供了线程池和定时任务执行的抽象接口:TaskExecutor和TaskScheduler来支持异步执行任务和定时执行任务功能.同时使用框架自己定义的抽象接口来屏蔽掉底层 ...
- 探究ElasticSearch中的线程池实现
探究ElasticSearch中的线程池实现 ElasticSearch里面各种操作都是基于线程池+回调实现的,所以这篇文章记录一下java.util.concurrent涉及线程池实现和Elasti ...
- 使用concurrent.futures模块中的线程池与进程池
使用concurrent.futures模块中的线程池与进程池 线程池与进程池 以线程池举例,系统使用多线程方式运行时,会产生大量的线程创建与销毁,创建与销毁必定会带来一定的消耗,甚至导致系统资源的崩 ...
- mina2中的线程池
一.Mina中的线程池模型 前面介绍了Mina总体的层次结构,那么在Mina里面是怎么使用Java NIO和进行线程调度的呢?这是提高IO处理性能的关键所在.Mina的线程调度原理主要如下图所示: A ...
- 【万字图文-原创】 | 学会Java中的线程池,这一篇也许就够了!
碎碎念 关于JDK源码相关的文章这已经是第四篇了,原创不易,粉丝从几十人到昨天的666人,真的很感谢之前帮我转发文章的一些朋友们. 从16年开始写技术文章,到现在博客园已经发表了222篇文章,大多数都 ...
- 《Java并发编程的艺术》 第9章 Java中的线程池
第9章 Java中的线程池 在开发过程中,合理地使用线程池能带来3个好处: 降低资源消耗.通过重复利用已创建的线程 降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创 ...
- java 中的线程池
1.实现下面的一个需求,控制一个执行函数只能被五个线程访问 package www.weiyuan.test; public class Test { public static void main( ...
随机推荐
- nginx 400
做服务器nginx配置的时候有出现过 400 Bad Request 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求.
- 微信小程序编写物流信息进度样式
做电商类型的小程序一定会碰到编写物流信息的时候,一般页面如下图 难点在于只有一条信息时候的页面样式 以及多条信息最后一条信息的页面样式 之前没做过这一块的东西,所以刚碰到的时候想了老半天orz.后来上 ...
- Message类的属性Msg所关联的消息ID
在做C#的Message消息处理的时候,用到了消息的msg编号不知道对应的是什么事件,所以才从网上找来资料如下,在文章最后我会给出资料的出处的. WM_NULL=0x0000 WM_CREATE=0x ...
- 微信无法跳转appstore总结--应用宝微下载申请
以前是有方法,可以实现微信下跳转appstore的. 大概就是把url改为:http://mp.weixin.qq.com/mp/redirect?url="跳转url"(可编码也 ...
- 跨域问题解决方案之chrome插件
地址: https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkki ...
- java中static的学习
1.static引入: 通常来说,当创建一个类是,就是在描述那个类的对象的外观与行为.除非用new创建那个类的对象,否则实际并未获取任何对象.当执行new来创建对象时,数据存储空间才被分配,七方法才供 ...
- 第五篇 Nginx的简单配置
先安装: sudo apt-get install nginx php5-fpm 我是在新安装的Ubuntu13上测试通过的,真的只安装这两个东西就够了. 然后编辑配置文件. sudo gedit / ...
- WPF中ToolTip的自定义
ToolTip或者PopUp这个控件在做界面时会经常用到.如何对ToolTip进行自定义呢? 1.首先自定义tooltip的controlTemplate,完全清除系统默认效果, 如下: ...
- FPGA中竞争冒险问题的研究
什么是竞争冒险? 1 引言 现场可编程门阵列(FPGA)在结构上由逻辑功能块排列为阵列,并由可编程的内部连线连接这些功能块,来实现一定的逻辑功能. FPGA可以替代其他PLD或者各种中小规模数 ...
- 反射ORM 三层(for sql server/mysql)
sql server and oracle 不同之处只在于: 1·参数@和: 2·自增和序列 RPROM.cs //(写错愕,应该是RPORM) RPROM.Insert(p1); //需求:DBS中 ...