【.NET Core】多线程之线程池(ThreadPool)详解(一)
文章目录
一、概述
有些应用程序使用多个线程,创建的线程花费大量时间处于休眠状态,等待事件发生,其他线程可能进入睡眠状态,并且仅定期被唤醒以轮询更改或更新状态信息,然后再次进入休眠状态。为了简化对这些线程的管理,.NET
框架为每一个进程提供了一个线程池,使应用程序能够根据需要来有效地利用对个线程。一个线程监视排到线程池的若干个等待操作的状态。让一个等待操作完成时,线程池中的一个辅助线程就会执行对应的回调函数。线程池中的线程由系统进行管理,程序员不需要费力于线程管理,可以几种精力处理应用程序任务。
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间之后创建另一个辅助线程。但线程的数目永远不会超过最大值。超过最大值的其他线程可以排队,但它们要等到其他线程完成后才启动。
二、线程池的应用范围
线程池特别适合于执行一些需要多个线程的任务。使用线程池能够优化这些任务的执行过程,从而提高吞吐量,它不仅能够使用系统针对此进程优化过程,而且还能够使系统针对计算机上的其他进程优化该执行过程。如果需要启动多个不同的任务,而不想分别设置没个线程的属性,则可以使用线程池。
线程池主要应用于以下几种情况:
- 不需要前台执行的线程
- 不需要在使用线程具有特定的优先级
- 线程的执行时间不易过长,否则会使线程阻塞。由于线程池具有最大线程数限制,因此大量阻塞的线程池的线程可能会阻止任务启动。
- 不需要将线程放入单线程单元。所有
ThreadPool
线程均不处于多线程单元中。 - 不需要具有与线程关联的稳定标识,或使某一线程专用于某一任务。
.NET
将线程池线程用于多种用途,包括任务并行库(TPL)操作、异步I/O
完成、计时器回调、注册等待操、使用委托的异步方法调用和System.Net
套接字连接。
三、线程池特性
线程池线程是后台线程。每个线程均使用默认的堆栈大小,以默认的优先级进行,并且位于多线程单元中。一旦线程池中的线程完成任务,它将返回到等待线程队列中。这时开始既可重用它。通过这种重复使用,应用程序可以避免生产为每个任务创建新线程的开销。
每个线程只有一个线程池。
3.1 线程池线程中的异常
线程池线程中未经处理的异常终止该进程。 以下为此规则的三种例外情况:
System.Threading.ThreadAbortException
在线程池线程中引发,因为调用了Thread.Abort
。Sytem.AppDomainUnloadedException
在线程池线程中引发,因为正在卸载应用程序域。- 公共语言运行时或主机进程将终止该线程
3.2 最大线程池线程数
可以排队到线程池中的操作数仅受可用内存限制。但是,线程池会限制进程中可同时处于活动状态的线程数。如果所有线程池线程都处于忙碌状态,则其他工作项将进行排队,直到要执行它们的线程空闲。进程的线程池的默认大小取决于若干因素。可以通过以下两个方法控制线程池的大小:
ThreadPool.GetMaxThreads
方法来获取线程池的最大线程数。ThreadPool.SetMaxThreads
方法来设置最大线程数。
3.3 最小值线程池线程数
线程池根据需要提供新的工作线程或 I/O 完成线程,直到它达到每个类别的指定最小值。 可以使用ThreadPool.GetMinThreads
方法来获取这些最小值。
达到最小值时,线程池可以创建其他线程或等待,直到一些任务完成。 线程池创建和销毁工作线程以优化吞吐量,吞吐量被定义为每个单位时间完成的任务数。 线程过少可能无法实现可用资源的最优利用,而线程过多则可能增加资源争用。
四、线程池使用
-
使用任务并行库(
TPL
)。默认情况下,TPL
类型使用线程池线程来运行任务。 -
通过从委托代码调用
ThreadPool.QueueUserWorkItem
并传递表示执行任务的方法的System.Threading.WaitCallback
委托来使用线程池。 -
使用线程池的另外一种方法
ThreadPool.RegisterWaitForSingleObject
方法并传递在发出信号或超时的时候调用System.Threading.WaitOrTimerCallback
委托所表示的方法System,Threading.WaitHandle
,从而对于等待操作相关的工作排队。线程池线程用于调用回调方法。