目录
一:特点
二:线程池中线程增加减少方式
三:方法
1.常用方法
拓展:工作者线程和I/O线程
2.其他方法
ThreadPool 是一个静态类
线程池可以看作一个容纳线程的容器 一个应用程序最多有一个线程池 在首次向线程池排入工作函数时自动创建
线程池可以设置最小线程数量和最大线程数量
可以复用线程 避免重复的销毁和创建 不能控制线程的调用和释放 默认为后台线程(即 IsBackground=true)优先级为ThreadPriority.Normal 每个线程都使用默认的堆栈大小
1.线程池被创建后或者任务数量小于最小值时 运行最小数量的空线程
2.当任务请求数量超过最小值 会在等待一段时间(大约500毫秒,从别的博客看的没有自己实验 有兴趣的可以自己试下,顺便给我说下结果)后创建新的线程去执行工作函数
3.当任务请求数量超过最大值 不再创建新的线程 会等待线程池中的某个线程结束后然后执行等待中的任务
4.当任务逐步完成小于最大值时 线程在空闲一段时间后(两分钟)后被释放并且回收相关资源
GetMaxThreads(Int32, Int32)
检索可以同时处于活动状态的线程池请求的数目。 所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。
参数1: 工作线程
参数2: I/O线程
GetMinThreads(Int32, Int32)
发出新的请求时,在切换到管理线程创建和销毁的算法之前检索线程池按需创建的线程的最小数量。
参数1: 工作线程
参数2: I/O线程
代码:
class Program {
static void Main(string[] args) {
int workerThread, ioThread;
ThreadPool.GetMaxThreads(out workerThread, out ioThread);
Console.WriteLine("最大工作者线程数:" + workerThread + " 最大io线程数:" + ioThread);
ThreadPool.GetMinThreads(out workerThread, out ioThread);
Console.WriteLine("最小工作者线程数:" + workerThread + " 最小io线程数:" + ioThread);
Console.ReadLine();
}
}
打印:
QueueUserWorkItem(WaitCallback)
将方法排入队列以便执行。 此方法在有线程池线程变得可用时执行。
参数1: public delegate void WaitCallback(object state);
QueueUserWorkItem(WaitCallback, Object)
将方法排入队列以便执行,并指定包含该方法所用数据的对象。 此方法在有线程池线程变得可用时执行。
参数1: public delegate void WaitCallback(object state);
参数2: callBack所使用的参数装箱成object
代码:
class Program {
static void Debug(object obj) {
Console.WriteLine("打印:" + (int)obj);
}
static void Main(string[] args) {
for(int i = 0; i < 10; i++)
ThreadPool.QueueUserWorkItem(Debug, (object)i);
Console.ReadLine();
}
}
打印:
RegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback, Object, Int32, Boolean)
注册一个等待 WaitHandle 的委托,并指定一个 32 位有符号整数来表示超时值(以毫秒为单位)。
参数1: EventWaitHandle ;
参数2: public delegate void WaitOrTimerCallback(object state, bool timedOut);
参数1: 外部传递过来的参数
参数2: 是否是超时
参数3: 参数2的第一个参数
参数4: 超时时间 如果设置成-1 参数2只在接收到信号后运行
如果设置成其他值 参数2在超时和接收到信号后运行
参数5: 是否循环调用 false 第一次运行结束后不再等待运行
true 每次完成后重记倒计时,直到注销等待
例1:
class Program {
static void FinishOrTimeOut(object obj, bool timeOut) {
Console.WriteLine("完成或者超时回调:" + (int)obj);
}
static void Main(string[] args) {
AutoResetEvent are = new AutoResetEvent(false);
RegisteredWaitHandle registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(are, FinishOrTimeOut, (object)1, 1000, false);
Console.ReadLine();
}
}
打印:
每隔1s打印一次 本次打印是超时回调
例2:
static void FinishOrTimeOut(object obj, bool timeOut) {
Console.WriteLine("完成或者超时回调:" + (int)obj);
}
static void Main(string[] args) {
AutoResetEvent are = new AutoResetEvent(false);
RegisteredWaitHandle registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(are, FinishOrTimeOut, (object)1, -1, true);
Console.WriteLine(".....");
are.Set();
Console.ReadLine();
}
}
打印:
本次回调是收到信号后的回调
注:超时回调还是收到信号后的回调在于
AutoResetEvent are = new AutoResetEvent(false);
are.Set();
下一篇将详细说一下 AutoResetEvent/ManualResetEvent
此处只做了解 are.Set()表示不用在等待了可以去执行回调委托了
SetMaxThreads(Int32, Int32)
设置可以同时处于活动状态的线程池的请求数目。 所有大于此数目的请求将保持排队状态,直到线程池线程变为可用。
参数1: 工作线程
参数2: I/O线程
SetMinThreads(Int32, Int32)
发出新的请求时,在切换到管理线程创建和销毁的算法之前设置线程池按需创建的线程的最小数量。
参数1: 工作线程
参数2: I/O线程
代码:
class Program {
static void Main(string[] args) {
int workerThread, ioThread;
if(ThreadPool.SetMaxThreads(8, 10)) {
ThreadPool.GetMaxThreads(out workerThread, out ioThread);
Console.WriteLine("最大工作者线程数:" + workerThread + " 最大io线程数:" + ioThread);
}
if(ThreadPool.SetMinThreads(1, 1)) {
ThreadPool.GetMinThreads(out workerThread, out ioThread);
Console.WriteLine("最小工作者线程数:" + workerThread + " 最小io线程数:" + ioThread);
}
Console.ReadLine();
}
}
打印:
IOCP:输入输出完成端口(Input/Output Completion Port,IOCP)
以下引用了其他博客中的内容
工作者线程:
用来完成一些计算的任务,在任务执行的过程中,需要CPU不间断地处理,所以,在工作者线程的执行过程中,CPU和线程的资源是充分利用的。
.NET中的术语工作者线程指的是任何线程而不是仅仅主线程。“工作者”的意思表示任何内容,包括等待IO端口完成,线程池会预先缓存一些工作者线程,因为创建线程的代价比较昂贵。
I/O线程:
主要用来完成输入和输出的工作,在这种情况下, 计算机需要I/O设备完成输入和输出的任务。在处理过程中,CPU是不需要参与处理过程的,此时正在运行的线程将处于等待状态,只有等任务完成后才会有事可做, 这样就造成线程资源浪费的问题。为了解决这样的问题,可以通过线程池来解决这样的问题,让线程池来管理线程。
.NET中的一些API方法,通过APM(异步编程模式),内部实现了ThreadPool.BindHandle方法。BeginXXX方法将用户的回调委托送到某个设备驱动程序,然后返回线程池。
当做完成后,OS会通过IOCP提醒CLR它工作已经完成,当接收到通知后,I/O线程会醒来并且运行用户的回调。
所以工作线程由开发人员调用,I/O线程由CLR调用。所以通常情况下,开发者并不会直接用到它。.
因此可以认为,工作者线程和I/O线程没有区别,它们都是普通的线程,但是CLR线程池中区分它们的目的是为了避免线程都去处理I/O回调而被耗尽,从而引发死锁。(设想,所有的工作者线程每一个都去等待I/O异步完成。)
开发人员需要关注的是确保I/O线程返回到线程池,I/O回调代码应该做尽量小的工作,并尽快返回到线程池。如果回调代码中的工作很多的话,应该考虑把工作拆分到一个工作者线程中去。否则,应用程序的风险是CLR线程池中保留I/O线程去做了工作者线程的活,可能导致死锁。
BindHandle(SafeHandle)
将操作系统句柄绑定到 ThreadPool。
GetAvailableThreads(Int32, Int32)
检索由 GetMaxThreads(Int32, Int32) 方法返回的最大线程池线程数和当前活动线程数之间的差值。
UnsafeQueueNativeOverlapped(NativeOverlapped)
将重叠的 I/O 操作排队以便执行。
UnsafeQueueUserWorkItem(WaitCallback, Object)
将指定的委托排队到线程池,但不会将调用堆栈传播到辅助线程。
UnsafeRegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback, Object, Int32, Boolean)
注册一个等待 WaitHandle 的委托,并使用一个 32 位带符号整数来表示超时时间(以毫秒为单位)。 此方法不将调用堆栈传播到辅助线程。