线程池ThreadPool的使用
创建线程需要时间。如果有不同的小任务需要完成,就可以事先创建许多线程,在应完成这些任务的时候发出请求。这个线程数最好在需要更多的线程时增加,在需要释放资源时减少。在.NET Framework中不需要自己创建这样一个列表,该列表由ThreadPool类托管。这个类会在需要时增减线程池中的线程数,直到最大的线程数。池中的最大线程数是可以配置的。在双核CPU中,默认设置为1023个工作线程和1000个I/O线程。也可以指定在创建线程池时应立即启动的最小线程数,以及线程池中可用的最大线程数。如果有更多的作业需要处理,线程池中的线程个数达到了极限,最新的作业就要排队,且必须等待线程完成其任务。
来看下面这个示例:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Threading;
6:
7: namespace Threadpool
8: {
9: class Program
10: {
11: static void Main(string[] args)
12: {
13: //用来保存工作线程的个数
14: int nWorkerThreads;
15: //用来保存I/O线程的个数
16: int nCompletionPortThreads;
17: //读取工作线程和I/O线程的最大线程数
18: ThreadPool.GetMaxThreads(out nWorkerThreads,out nCompletionPortThreads);
19: //把这些信息写入到控制台
20: Console.WriteLine("Max worker threads:{0},"+
21: "I/O completion threads:{1}",
22: nWorkerThreads,nCompletionPortThreads);
23: //在for循环中,调用ThreadPool.QueueUserWorkItem()方法,传递一个WaitCallback类型的委托,把JobForAThread()方法赋予线程池中的线程
24: //线程池收到这个请求后,就会从池中选择一个线程,来调用该方法。如果线程池还没有运行,就会创建一个线程池,并启动第一个线程。如果线程池已经在
25: //运行,且有一个空闲线程来完成该任务,就把该作业传递给这个线程。
26: for (int i = 0; i < 5;i++)
27: {
28: ThreadPool.QueueUserWorkItem(JobForThread);
29: }
30: Console.ReadKey();
31: }
32:
33: static void JobForThread(object state)
34: {
35: for(int i=0;i<3;i++)
36: {
37: Console.WriteLine("loop {0},running inside pooled thread {1}", i, Thread.CurrentThread.ManagedThreadId);
38: Thread.Sleep(50);
39: }
40: }
41: }
42: }
运行结果如下所示:
由运行结果可以看出5个作用只有线程池中的两个线程处理。读者运行该程序的结果可能与此不同,也可以改变作业的睡眠时间和要处理的作业数,得到完全不同的结果。
线程池使用起来很简单,但是它有一些限制:
1)、线程池中的所有线程都是后台线程。如果进程的所有前台线程都结束了,所有的后台线程就会停止。不能把入池的线程改为前台线程。
2)、不能给入池的线程设置优先级或名称。
3)、对于COM对象,入池的所有线程都是多线程单元(multithreaded apartment,MTA)线程。许多COM对象都需要单线程单元(single-threaded apartment,MTA)线程。
4)、入池的线程只能用于时间较短的任务。如果线程要一直运行(如word的拼写检查器线程),就应该使用
Thread类创建一个线程。