线程池相当于一个缓存的概念,在该池中已经存在了一些没有被销毁的线程,而当应用程序需要一个新的线程时,就可以从线程池中直接获取一个已经存在的线程。相对应的,当一个线程被使用完毕后并不会立刻被销毁,而是放入线程池中等待下一次使用。
.NET中的线程池由CLR管理,管理的策略是灵活可变的,因此线程池中的线程数量也是可变的,使用者只需向线程池提交需求即可,下图则直观地展示了CLR是如何处理线程池需求的:
PS:线程池中运行的线程均为后台线程(即线程的 IsBackground 属性被设为true),所谓的后台线程是指这些线程的运行不会阻碍应用程序的结束。相反的,应用程序的结束则必须等待所有前台线程结束后才能退出。
(2)在.NET中使用线程池
在.NET中通过 System.Threading.ThreadPool 类型来提供关于线程池的操作,ThreadPool 类型提供了几个静态方法,来允许使用者插入一个工作线程的需求。常用的有以下三个静态方法:
① static bool QueueUserWorkItem(WaitCallback callback)
② static bool QueueUserWorkItem(WaitCallback callback, Object state)
③ static bool UnsafeQueueUserWorkItem(WaitCallback callback, Object state)
有了这几个方法,我们只需要将线程要处理的方法作为参数传入上述方法即可,随后的工作都由CLR的线程池管理程序来完成。其中,WaitCallback 是一个委托类型,该委托方法接受一个Object类型的参数,并且没有返回值。下面的代码展示了如何使用线程池来编写多线程的程序:
class Program { static void Main(string[] args) { ; result = ThreadPool.QueueUserWorkItem(DoWork, taskInfo); (!result) { Console.WriteLine(); } else { Console.WriteLine(); } Console.ReadKey(); } DoWork(object state) { (int i = 0; i < 10; i++) { Console.WriteLine(, state); Thread.Sleep(1000); } } }
View Code上述代码执行后,如果不输入任何字符,那么会得到如下图所示的执行结果:
PS:事实上,UnsafeQueueWorkItem方法实现了完全相同的功能,二者的差别在于UnsafeQueueWorkItem方法不会将调用线程的堆栈传递给辅助线程,这就意味着主线程的权限限制不会传递给辅助线程。UnsafeQueueWorkItem由于不进行这样的传递,因此会得到更高的运行效率,但是潜在地提升了辅助线程的权限,也就有可能会成为一个潜在的安全漏洞。
2.3 如何查看和设置线程池的上下限?线程池的线程数是有限制的,通常情况下,我们无需修改默认的配置。但在一些场合,我们可能需要了解线程池的上下限和剩余的线程数。线程池作为一个缓冲池,有着其上下限。在通常情况下,当线程池中的线程数小于线程池设置的下限时,线程池会设法创建新的线程,而当线程池中的线程数大于线程池设置的上限时,线程池将销毁多余的线程。
PS:在.NET Framework 4.0中,每个CPU默认的工作者线程数量最大值为250个,最小值为2个。而IO线程的默认最大值为1000个,最小值为2个。
在.NET中,通过 ThreadPool 类型提供的5个静态方法可以获取和设置线程池的上限和下限,同时它还额外地提供了一个方法来让程序员获知当前可用的线程数量,下面是这五个方法的签名:
① static void GetMaxThreads(out int workerThreads, out int completionPortThreads)
② static void GetMinThreads(out int workerThreads, out int completionPortThreads)
③ static bool SetMaxThreads(int workerThreads, int completionPortThreads)
④ static bool SetMinThreads(int workerThreads, int completionPortThreads)
⑤ static void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
下面的代码示例演示了如何查询线程池的上下限阈值和可用线程数量:
class Program { static void Main(string[] args) { // 打印阈值和可用数量 GetLimitation(); GetAvailable(); // 使用掉其中三个线程 Console.WriteLine(); ThreadPool.QueueUserWorkItem(Work); ThreadPool.QueueUserWorkItem(Work); ThreadPool.QueueUserWorkItem(Work); Thread.Sleep(1000); // 打印阈值和可用数量 GetLimitation(); GetAvailable(); // 设置最小值 Console.WriteLine(); ThreadPool.SetMinThreads(10, 10); // 打印阈值 GetLimitation(); Console.ReadKey(); } Work(object o) { Thread.Sleep(10 * 1000); } GetLimitation() { int maxWork, minWork, maxIO, minIO; // 得到阈值上限 ThreadPool.GetMaxThreads(out maxWork, out maxIO); // 得到阈值下限 ThreadPool.GetMinThreads(out minWork, out minIO); // 打印阈值上限 Console.WriteLine(, maxWork.ToString(), maxIO.ToString()); // 打印阈值下限 Console.WriteLine(, minWork.ToString(), minIO.ToString()); Console.WriteLine(); } GetAvailable() { int remainWork, remainIO; // 得到当前可用线程数量 ThreadPool.GetAvailableThreads(out remainWork, out remainIO); // 打印可用线程数量 Console.WriteLine(, remainWork.ToString(), remainIO.ToString()); Console.WriteLine(); } }
View Code该实例的执行结果如下图所示: