2014-07-06
26.1 CLR线程池基础
如25章所述,创建和销毁线程是一个比较昂贵的操作:
- 太多的线程也会浪费内存资源。
- 由于操作系统必须调度可运行的线程并执行上下文切换,所以太多的线程还有损于性能。
为了改善这个情况,CLR使用了代码来管理它自己的线程池。可将线程池想像成可由你的应用程序使用的一个线程集合。每个进程都有一个线程池,它在各个应用程序域(AppDomain)是共享的.
线程池是如何工作的:
- CLR初始化时,线程池是没有线程的。在内部,线程池维护了一个操作请求队列。应用程序想执行一个异步操作时,就调用某个方法,将一个记录项(entry)追加到线程池的队列中。线程池的代码从这个队列中提取记录项,将这个记录项派遣(dispatch)给一个线程池线程。如果线程池中没有线程,就创建新的线程。创建线程要产生一定的性能损失。然而,当线程池完成任务后,线程不会被销毁。相反,线程会返回线程池,在那里进入空闲状态,等待响应另一个请求。由于线程不销毁自身,所以不再产生额外的性能损失。
- 如果你的应用程序向线程池发出许多请求,线程池会尝试只用一个线程来服务所有的请求。然而,如果你的应用程序发出请求的速度超过了线程池处理它们的速度,就会创建额外的线程。最终,你的应用程序所有请求都可能有少量的线程处理,所有线程池不必创建大量的线程。
- 如果你的应用程序停止向线程池发出请求,池中含有大量空闲的线程。这是对内存资源的一种浪费。所以,当一个线程池线程空闲一段时间以后,线程会自己醒来终止自己以释放资源。
在内部,线程池将自己的线程划分为工作者(Worker)线程和I/O线程。应用程序要求线程池执行一个异步的计算限制操作时(这个操作可能发起一个I/O限制的操作),使用的就是工作者线程。I/O线程用于通知你的代码一个异步I/O限制操作已经完成,具体的说,这意味着使用"异步编程模型"发出I/O请求,比如访问文件、网络服务器、数据库等等。
26.2 执行简单的计算限制操作
将一个异步的、计算限制的操作放到一个线程池的队列中,通常可以调用ThreadPool类定义的以下方法之一:
1 static Boolean QueueUserWorkItem(WaitCallback callBack); 2 static Boolean QueueUserWorkItem(WaitCallback callBack,Object state);
这些方法向线程池的队列中添加一个"工作项"(work item)以及可选的状态数据, 如果此方法成功排队,则为 true;如果无法将该工作项排队,则引发 OutOfMemoryException。工作项其实就是由callBack参数标识的一个方法,该方法将由线程池线程调用。可通过state实参(状态数据)向方法传递一个参数。无state参数的那个版本的QueueUserWorkItem则向回调方法传递null。最终,池中的某个线程会处理工作项,造成你指定的方法被调用。你写的回调方法必须匹配System.Threading.WaitCallBack委托类型,它的定义如下:
delegate void WaitCallback(Object state);
注意:WaitCallback委托、TimerCallback委托(在本章26.8节“执行时计算限制操作”中讨论)和ParameterizedThreadStart委托(在第25章“线程基础”中讨论)是完全一致的。因此,只要定义一个和该签名匹配的方法后,使用ThreadPool.QueueUserWorkItem方法,使用System.Threading.Timer对象,或着使用System.Threading.Thread对象,都一个调用这个方法。
以下演示了如何让一个线程池线程以异步方式调用一个方法:
1 using System; 2 using System.Threading; 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Console.WriteLine("Main thread: queuing an asynchronous operation"); 8 ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5); 9 Console.WriteLine("Main thread: Doing other work here..."); 10 Thread.Sleep(10000); // 模拟其它工作 (10 秒钟) 11 //Console.ReadLine(); 12 } 13 14 // 这是一个回调方法,必须和WaitCallBack委托签名一致 15 private static void ComputeBoundOp(Object state) 16 { 17 // 这个方法通过线程池中线程执行 18 Console.WriteLine("In ComputeBoundOp: state={0}", state); 19 Thread.Sleep(1000); // 模拟其它工作 (1 秒钟) 20 21 // 这个方法返回后,线程回到线程池,等待其他任务 22 } 23 }