线程池 可以使用线程池来根据应用程序的需要更为有效地利用多个线程。许多应用程序使用多个线程,但这些线程经常在休眠状态中耗费大量的时间来等待事件发生。其他线程可能进入休眠状态,并且仅定期被唤醒以轮询更改或更新状态信息,然后再次进入休眠状态。使用线程池就可以为应用程序提供一个由系统管理的辅助线程池,从而使您可以集中精力于应用程序任务而不是线程管理。实际上,如果要执行一些需要多个线程的较短任务,则使用 ThreadPool 类是利用多个线程的最方便且最好的方法。使用线程池能够优化这些任务的执行过程,从而提高吞吐量,它不仅能够使系统针对此进程优化该执行过程,而且还能够使系统针对计算机上的其他进程优化该执行过程,即使您的应用程序对这些进程一无所知,系统也能做到这一点。使用线程池使系统能够在考虑到计算机上的所有当前进程后对线程时间片进行优化。 .NET Framework 出于以下几个目的使用线程池:异步调用、System.Net 套接字连接、异步 I/O 完成以及计时器与注册的等待操作等等。 通过从托管代码调用 ThreadPool.QueueUserWorkItem(或者从非托管代码调用 CorQueueUserWorkItem)并传递用来包装要添加到队列中的方法的 WaitCallback 委托来使用线程池。也可以通过使用 ThreadPool.RegisterWaitForSingleObject 并传递 WaitHandle(在向其发出信号或超时时,它将引发对由 WaitOrTimerCallback 委托包装的方法的调用)来将与等待操作相关的工作项排队到线程池中。在这两种情况下,线程池都使用或创建一个后台线程来调用回调方法。 如果您知道调用方的堆栈与在排队任务执行期间执行的所有安全检查不相关,则还可以使用不安全的方法 ThreadPool.UnsafeQueueUserWorkItem 和 ThreadPool.UnsafeRegisterWaitForSingleObject。QueueUserWorkItem 和 RegisterWaitForSingleObject 都会捕获调用方的堆栈,此堆栈将在线程池线程开始执行任务时合并到线程池线程的堆栈中。如果需要进行安全检查,则必须检查整个堆栈。尽管此检查提供了安全,但它还具有一定的性能开销。使用“不安全的”方法调用并不会提供绝对的安全,但它会提供更好的性能。 每个进程只有一个 ThreadPool 对象。线程池在您第一次调用 ThreadPool.QueueUserWorkItem 时创建,或者在一个计时器或注册的等待操作将一个回调方法排入队列时创建。一个线程监视所有已排队到线程池中的任务。当某项任务完成后,线程池中的线程将执行相应的回调方法。在对一个工作项进行排队之后将无法取消它。 可以排队到线程池中的操作的数目仅受可用内存的限制;但是,线程池将对允许在进程中同时处于活动状态的线程数目强制实施限制(这取决于 CPU 的数目和其他因素)。每个线程都使用默认堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间之后创建另一个辅助线程。但线程的数目永远不会超过最大值。在执行 ThreadPool 回调时,ThreadPool 还切换到正确的 AppDomain。 在若干种情况下,适合于创建并管理自己的线程而不是使用 ThreadPool。您应在以下几种情况下创建并管理自己的线程: • 如果您需要使一个任务具有特定的优先级。 • 如果您具有可能会长时间运行(并因此阻塞其他任务)的任务。 • 如果您需要将线程放置到单线程单元中(所有 ThreadPool 线程均处于多线程单元中)。 • 如果您需要与线程关联的永久标识。例如,您可能想使用专用线程来中止该线程、将其挂起或按名称发现它。 using System; using System.Threading; namespace ThreadPool6 { /**////<summary> /// Class1 的摘要说明。 ///</summary> class Class1 { /**////<summary> /// 应用程序的主入口点。 ///</summary> [STAThread] staticvoid Main(string[] args) { // // TODO: 在此处添加代码以启动应用程序 // Console.WriteLine("ThreadPool Test") ; string strSel="3"; while(strSel.ToUpper()!="E") { if(strSel.ToUpper() =="A") { Console.WriteLine(@"本示例将一个由 ThreadProc 方法表示的非常简单的任务排入队列,使用的是 QueueUserWorkItem。 ") ; // Queue the task. ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc)); Console.WriteLine("Main thread does some work, then sleeps."); // If you comment out the Sleep, the main thread exits before // the thread pool task runs. The thread pool uses background // threads, which do not keep the application running. (This // is a simple example of a race condition.) Thread.Sleep(1000); Console.WriteLine("Main thread exits."); } elseif(strSel.ToUpper()=="B" ) { Console.WriteLine("开始创建100个线程池对象") ; C_ThreadPoolTest[] cTpt=new C_ThreadPoolTest[100] ; for(int i=0;i<100;i++) { cTpt[i]=new C_ThreadPoolTest(i.ToString() ) ; ThreadPool.QueueUserWorkItem(new WaitCallback(cTpt[i].PrintResult ) ) ; } Console.WriteLine("100个线程池对象创建完毕,主线程开始休眠 10秒钟") ; Thread.Sleep(10000) ; //10秒钟 Console.WriteLine("主线程休眠 10秒钟 结束") ; } elseif(strSel.ToUpper()=="C") { Console.WriteLine("为 QueueUserWorkItem 提供任务数据。即通过线程池可以访问到被执行类中的相关参数") ; // Create an object containing the information needed // for the task. TaskInfo ti =new TaskInfo("This report displays the number {0}.", 42); // Queue the task and data. if (ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc2), ti)) { //WaitCallback : 通过将 WaitCallback 委托传递给 ThreadPool.QueueUserWorkItem 来将任务排入队列以便执行。您的回调方法将在某个线程池线程可用时执行。 Console.WriteLine("Main thread does some work, then sleeps."); // If you comment out the Sleep, the main thread exits before // the ThreadPool task has a chance to run. ThreadPool uses // background threads, which do not keep the application // running. (This is a simple example of a race condition.) Thread.Sleep(1000); Console.WriteLine("Main thread exits."); } else { Console.WriteLine("Unable to queue ThreadPool request."); } } elseif(strSel.ToUpper()=="D") { Console.WriteLine(@"下面的示例演示了几种线程处理功能。 1.使用 RegisterWaitForSingleObject 将需要执行的任务以 ThreadPool 线程的方式排队。 2.使用 AutoResetEvent 发出信号,通知执行任务。 3.用 WaitOrTimerCallback 委托处理超时和信号。 4.用 RegisteredWaitHandle 取消排入队列的任务。") ; // The main thread uses AutoResetEvent to signal the // registered wait handle, which executes the callback // method. AutoResetEvent ev =new AutoResetEvent(false); //注册等待事件 TaskInfo2 ti =new TaskInfo2(); ti.OtherInfo ="First task"; // The TaskInfo for the task includes the registered wait // handle returned by RegisterWaitForSingleObject. This // allows the wait to be terminated when the object has // been signaled once (see WaitProc). ti.Handle = ThreadPool.RegisterWaitForSingleObject( ev, new WaitOrTimerCallback(WaitProc), ti, 1000, false//当它为真时,只才刚开始委托处理超时和信号时执行WaitProc后面的超时将不作处理; ); // The main thread waits three seconds, to demonstrate the // time-outs on the queued thread, and then signals. Thread.Sleep(3100); Console.WriteLine("Main thread signals."); ev.Set(); // The main thread sleeps, which should give the callback // method time to execute. If you comment out this line, the // program usually ends before the ThreadPool thread can execute. Thread.Sleep(1000); // If you start a thread yourself, you can wait for it to end // by calling Thread.Join. This option is not available with // thread pool threads. } elseif(strSel.ToUpper()=="W") { Test2(); } Console.WriteLine("Please select: A : [MsdnSample] B: [MyTestSample] C : [为 QueueUserWorkItem 提供任务数据] D : [RegisterWaitForSingleObject用法] W : [自定义的线程RegisterWaitForSingleObject操作] E : [Exit] ") ; strSel=Console.ReadLine(); } } // The callback method executes when the registered wait times out, // or when the WaitHandle (in this case AutoResetEvent) is signaled. // WaitProc unregisters the WaitHandle the first time the event is // signaled. publicstaticvoid WaitProc(object state, bool timedOut) { // The state object must be cast to the correct type, because the // signature of the WaitOrTimerCallback delegate specifies type // Object. TaskInfo2 ti = (TaskInfo2) state; string cause ="TIMED OUT"; if (!timedOut) { cause ="SIGNALED"; // If the callback method executes because the WaitHandle is // signaled, stop future execution of the callback method // by unregistering the WaitHandle. if (ti.Handle !=null) ti.Handle.Unregister(null); //取消排入队列的任务 } Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.", ti.OtherInfo, Thread.CurrentThread.GetHashCode().ToString(), cause ); } staticvoid Test2() { C_ThreadPoolTest[] TPtest =new C_ThreadPoolTest[100] ; AutoResetEvent[] startEvent =new AutoResetEvent[10] ; //通知正在等待的线程已发生事件。如果初始状态为终止状态,则为 true;否则为 false。 //AutoResetEvent 允许线程通过发信号互相通信。通常,此通信涉及线程需要独占访问的资源。 //线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号。如果 AutoResetEvent 处于非终止状态,则该线程阻塞, //并等待当前控制资源的线程通过调用 Set 发出资源可用的信号。 //调用 Set 终止 AutoResetEvent 以释放等待线程。AutoResetEvent 将保持终止状态,直到一个正在等待的线程被释放, //然后自动返回非终止状态。如果没有任何线程在等待,则状态将无限期地保持为终止状态。 Console.WriteLine( "--- 开始创建等待事件,并将其初始值设为执行 ---" ) ; for( int i=0 ; i<10 ; i++ ) { startEvent[i] =new AutoResetEvent( false ) ; } Console.WriteLine( "--- Start creating objects ---" ) ; for( int i=0 ; i<100 ; i++ ) { TPtest[i]=new C_ThreadPoolTest( i.ToString()) ; ThreadPool.RegisterWaitForSingleObject( startEvent[i/10], new WaitOrTimerCallback( TPtest[i].PrintResult ), //超时等待 null, 1000, true ) ; //最后一个参数设为executeOnlyOnce=false时将会对注册在进线程池中处于等待状态的线程,循环回调,没完没了. } Console.WriteLine( "--- Objects created ---" ) ; Console.WriteLine( "--- Fire events ---" ) ; for( int i=0 ; i<10 ; i++ ) { Console.WriteLine( "终止产生的等待事件: {0} ,以释放等待线程", i ) ; startEvent[i].Set() ; Thread.Sleep( 2000 ) ; } Thread.Sleep( 2000 ) ; } // This thread procedure performs the task. staticvoid ThreadProc(Object stateInfo) { // No state object was passed to QueueUserWorkItem, so // stateInfo is null. Console.WriteLine("Hello from the thread pool."); } /**///////////////////////////// // The thread procedure performs the independent task, in this case // formatting and printing a very simple report. // staticvoid ThreadProc2(Object stateInfo) { TaskInfo ti = (TaskInfo) stateInfo; Console.WriteLine(ti.Boilerplate, ti.Value); } } // TaskInfo contains data that will be passed to the callback // method. publicclass TaskInfo2 { public RegisteredWaitHandle Handle =null; publicstring OtherInfo ="default"; } publicclass C_ThreadPoolTest { string ThreadName=""; double iCount=0; public C_ThreadPoolTest(string name) { ThreadName=name; } privatedouble Calc( ) { double j=0; iCount=0; for(double i=0;i<100;i++) { j+=i; if (i%5==0) iCount+=1; } return j; } publicvoid PrintResult(object obj1) { Console.WriteLine( "\nThread "+ThreadName+" 计算完毕."); double x1=Calc( ); Console.WriteLine("\n计算次数: "+ iCount.ToString() ) ; Console.WriteLine("\n计算结果: "+ x1.ToString()); } publicvoid PrintResult(object obj1, bool bl1) { Console.WriteLine( "\nThread "+ThreadName+" 计算完毕."); double x1=Calc( ); Console.WriteLine("\n计算次数: "+ iCount.ToString() ) ; Console.WriteLine("\n计算结果: "+ x1.ToString()); } } publicclass TaskInfo { // State information for the task. These members // can be implemented as read-only properties, read/write // properties with validation, and so on, as required. publicstring Boilerplate; //样板文件 publicint Value; // Public constructor provides an easy way to supply all // the information needed for the task. public TaskInfo(string text, int number) { Boilerplate = text; Value = number; } } }