ThreadPool类为应用程序提供一个由系统管理的辅助线程池,从而使您可以集中精力于应用程序任务而不是线程管理。如果您具有需要后台处理的短期任务,则托管线程池是可以利用多个线程的便捷方式。
说明 |
从 .NET Framework 2.0 Service Pack 1 开始,线程池的吞吐量在三个关键方面得到了显著提高:让任务排队、调度线程池线程和调度 I/O 完成线程,在 .NET Framework 的早期版本中这三个方面被视为瓶颈。若要使用此功能,您的应用程序应面向 .NET Framework 3.5 版。有关更多信息,请参见.NET Framework Architecture。 |
对于与用户界面交互的后台任务,.NET Framework 2.0 版还提供了BackgroundWorker类,该类可以使用用户界面线程中引发的事件进行通信。
.NET Framework 使用线程池线程实现多种用途,包括异步 I/O 完成、Timer 回调、注册的等待操作、使用委托的异步方法调用以及System.Net套接字连接。
在以下几种情况下,适合于创建并管理自己的线程而不是使用线程池线程:
-
需要前台线程。
-
需要使线程具有特定的优先级。
-
您的任务会导致线程长时间被阻塞。由于线程池具有最大线程数限制,因此大量阻塞的线程池线程可能会阻止任务启动。
-
需要将线程放入单线程单元。所有ThreadPool线程均处于多线程单元中。
-
您需要具有与线程关联的稳定标识,或使某一线程专用于某一任务。
线程池线程是后台线程。请参见前台和后台线程。每个线程都使用默认堆栈大小,以默认的优先级运行,并处于多线程单元中。
每个进程只有一个线程池对象。
线程池线程中的异常
线程池线程中未经处理的异常将终止进程。以下为此规则的三种例外情况:
有关更多信息,请参见托管线程中的异常。
在 .NET Framework 1.0 和 1.1 版中,公共语言运行时将捕获线程池线程中的未经处理的异常,而不出现任何提示。这可能会破坏应用程序状态,并最终导致应用程序挂起,将很难进行调试。
最大线程池线程数
可排队到线程池的操作数仅受可用内存的限制;但是,线程池限制进程中可以同时处于活动状态的线程数。默认情况下,限制每个 cpu 只能使用 250 个辅助线程和 1,000 个 I/O 完成线程。
通过使用GetMaxThreads和SetMaxThreads方法可以控制最大线程数。
在 .NET Framework 1.0 和 1.1 版中,不能从托管代码中设置线程池大小。承载公共语言运行时的代码可以使用 mscoree.h 中定义的CorSetMaxThreads设置该大小。
最小空闲线程数
即使是在所有线程都处于空闲状态时,线程池也会维持最小的可用线程数,以便队列任务可以立即启动。将终止超过此最小数目的空闲线程,以节省系统资源。默认情况下,每个处理器维持一个空闲线程。
在启动新的空闲线程之前,线程池具有一个内置延迟(在 .NET Framework 2.0 版中为半秒钟)。应用程序在短期内定期启动许多任务时,空闲线程数的微小增加会导致吞吐量显著增加。将空闲线程数设置得过高会浪费系统资源。
使用GetMinThreads和SetMinThreads方法可以控制线程池所维持的空闲线程数。
在 .NET Framework 1.0 版中,不能设置最小空闲线程数。
线程池还提供了ThreadPool.UnsafeQueueUserWorkItem和ThreadPool.UnsafeRegisterWaitForSingleObject方法。仅在确定调用方的堆栈与执行排队任务的过程中执行的任何安全检查无关时使用这些方法。QueueUserWorkItem和RegisterWaitForSingleObject都捕获调用方的堆栈,在线程开始执行任务时将此堆栈合并到线程池线程的堆栈中。如果需要进行安全检查,则必须检查整个堆栈。尽管此检查提供了安全,但它还具有一定的性能开销。
使用线程池的方式是,从托管代码调用ThreadPool.QueueUserWorkItem(或从非托管代码调用CorQueueUserWorkItem)并传递表示执行任务的方法的WaitCallback委托。也可以通过使用ThreadPool.RegisterWaitForSingleObject方法并传递WaitHandle(在向其发出信号或超时时,它将引发对WaitOrTimerCallback委托表示的方法的调用)来将与等待操作相关的工作项排入队列。在这两种情况下,线程池都使用一个后台线程来调用回调方法。
以下三个代码示例演示RegisterWaitForSingleObject方法。
第一个示例使用QueueUserWorkItem方法将一个由ThreadProc方法表示的非常简单的任务排入队列。
Imports System
Imports System.Threading
Public Class Example
Public Shared Sub Main()
' Queue the task.ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf 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.")
End Sub
' This thread procedure performs the task.Shared Sub ThreadProc(stateInfo As Object)
' No state object was passed to QueueUserWorkItem,so
' stateInfo is null.Console.WriteLine("Hello from the thread pool.")
End Sub
End Class
为 QueueUserWorkItem 提供任务数据
下面的代码示例使用QueueUserWorkItem方法将一个任务排队并为该任务提供数据。
Imports System
Imports System.Threading
' TaskInfo holds state information for a task that will be
' executed by a ThreadPool thread.Public Class 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.Public Boilerplate As String
Public Value As Integer
' Public constructor provides an easy way to supply all
' the information needed for the task.Public Sub New(text As String,number As Integer)
Boilerplate = text
Value = number
End Sub
End Class
Public Class Example
Public Shared Sub Main()
' Create an object containing the information needed
' for the task.Dim ti As New TaskInfo("This report displays the number {0}.",42)
' Queue the task and data.If ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf ThreadProc),ti) Then
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.")
End If
End Sub
' The thread procedure performs the independent task,in this case
' formatting and printing a very simple report.
'Shared Sub ThreadProc(stateInfo As Object)
Dim ti As TaskInfo = CType(stateInfo,TaskInfo)
Console.WriteLine(ti.Boilerplate,ti.Value)
End Sub
End Class
RegisterWaitForSingleObject
下面的示例演示几种线程处理功能。
Imports System
Imports System.Threading
' TaskInfo contains data that will be passed to the callback
' method.Public Class TaskInfo
public Handle As RegisteredWaitHandle = Nothing
public OtherInfo As String = "default"
End Class
Public Class Example
Public Shared Sub Main()
' The main thread uses AutoResetEvent to signal the
' registered wait handle,which executes the callback
' method.Dim ev As New AutoResetEvent(false)
Dim ti As New TaskInfo()
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(AddressOf WaitProc),_
ti,_
1000,_
false _
)
' The main thread waits about three seconds,to demonstrate
' the time-outs on the queued task,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.End Sub
' 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.Public Shared Sub WaitProc(state As Object,timedOut As Boolean)
' The state object must be cast to the correct type,because the
' signature of the WaitOrTimerCallback delegate specifies type
' Object.Dim ti As TaskInfo = CType(state,TaskInfo)
Dim cause As String = "TIMED OUT"
If Not timedOut Then
cause = "SIGNALED"
' If the callback method executes because the WaitHandle is
' signaled,stop future execution of the callback method
' by unregistering the WaitHandle.If Not ti.Handle Is Nothing Then
ti.Handle.Unregister(Nothing)
End If
End If
Console.WriteLine("WaitProc( {0} ) executes on thread {1}; cause = {2}.",_
ti.OtherInfo,_
Thread.CurrentThread.GetHashCode().ToString(),_
cause _
)
End Sub
End Class