线程池
.NET Compact Framework提供了几种不同的方法去创建多线程应用程序。而实现多线程最理想的方法可能得算使用线程池中已存在的线程。图23-2展示使用线程池中已存在的线程和直接创建线程在执行效率上的差别。
图23-2:使用线程池和线程的比较
如图23-2所示的测试程序运行在普通的Windows CE .NET 4.2模拟器中,并创建200个生存期都很短的相互不同的工作线程。而实际上,所有工作线程的唯一功能是检查某个具体线程是否为第200个线程。在此情况下,设置一个外部事件来通知主线程已完成测试的运行,这样主线程就可以将计时信息显示在用户界面上。清单23-6是如图23-2所示的测试程序的实现代码。线程对应btnUseThread_Click过程,线程池对应btnUseThreadPool_Click过程。
清单23-6:比较线程池和线程的执行效率
Public Class ThreadPoolVSThread Dim threadCounter As Integer Dim threadPoolCounter As Integer Dim doneEvent As Threading.AutoResetEvent Private Sub btnUseThread_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnUseThread.Click threadCounter = 0 doneEvent = New Threading.AutoResetEvent(False) lThreadResult.Text = "" Dim elapsedTime As Integer = Environment.TickCount Dim i As Integer For i = 1 To 200 Dim workerThread As Threading.Thread = _ New Threading.Thread(New Threading.ThreadStart(AddressOf MyWorkerThread)) workerThread.Start() Next doneEvent.WaitOne() elapsedTime = Environment.TickCount - elapsedTime lThreadResult.Text = "创建线程:" + elapsedTime.ToString() + " msec" End Sub Private Sub MyWorkerThread() threadCounter += 1 If (threadCounter = 200) Then doneEvent.Set() End If End Sub Private Sub btnUseThreadPool_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnUseThreadPool.Click threadPoolCounter = 0 doneEvent = New Threading.AutoResetEvent(False) lThreadPoolResult.Text = "" Dim elapsedTime As Integer = Environment.TickCount Dim i As Integer For i = 1 To 200 Threading.ThreadPool.QueueUserWorkItem( _ New Threading.WaitCallback(AddressOf MyWaitCallBack)) Next doneEvent.WaitOne() elapsedTime = Environment.TickCount - elapsedTime lThreadPoolResult.Text = "创建线程:" + elapsedTime.ToString() + " msec" End Sub Private Sub MyWaitCallBack(ByVal stateInfo As Object) threadPoolCounter += 1 If (threadPoolCounter = 200) Then doneEvent.Set() End If End Sub End Class
从图23-2所示界面的执行结果来看,使用线程池线程所占时间不到使用Thread线程所占时间的10%。原因是线程池中的线程是可重复使用的,从而消除创建新线程对象和设置其资源的系统开销。创建工作线程是一项系统开销较大的操作,也是导致性能差异的重要因素。线程池线程在需要时被创建,直至达到200的线程数量上线。当回调函数被终止时,线程池线程大约驻留线程池60秒以等待重用。如果在60秒内应用程序再次运行再次使用线程的话,这时线程池中存在足够的线程可被重用而无需创建新线程,因此大大提高了应用程序执行性能。实际上,线程池就是一个可重复使用的线程集合。
因为线程池线程是共享资源,所以不应该在线程池内运行长期的线程。如果调用QueueUserWorkItem方 法时线程池中没有可用的线程,则要么在线程池中创建新线程要么等待直到线程池中的线程变得可用后才开始执行指定的委托(线程池中的全部线程均被使用时)。 线程池线程是一个可以直接运行且实际存在的前台线程,需要在应用程序关闭之前将其正确终止,正确终止线程池线程的方法与普通线程类似。