Microsoft Windows 中的托管和非托管线程处理
所有线程的管理都是通过 Thread 类完成的,包括由公共语言运行时创建的线程以及在运行时以外创建并进入托管环境以执行代码的线程。 运行时监视其进程中曾经在托管执行环境中执行过代码的所有线程。 它不跟踪任何其他线程。 线程可以通过 COM 互操作(原因是运行时将托管对象作为 COM 对象向非托管领域公开)、COM DllGetClassObject() 函数和平台调用进入托管执行环境。
当非托管线程进入运行时时(如通过 COM 可调用包装),系统将检查该线程的线程本地存储区以查找内部托管 Thread 对象。 若找到一个对象,运行时就注意到该线程。 但如果一个也找不到,则运行时将生成新的 Thread 对象并将其安装在该线程的线程本地存储区中。
在托管线程处理中,Thread.GetHashCode 是稳定的托管线程标识。 在线程的生存期内,它不会与来自其他任何线程的值相冲突,不管您是从哪个应用程序域获取该值。
注意 |
---|
因为非托管宿主可以控制托管线程和非托管线程之间的关系,所以操作系统 ThreadId 与托管线程之间没有固定的关系。具体地说,一个复杂的宿主可以使用 Fiber API 针对同一操作系统线程调度多个托管线程,或在不同的操作系统线程之间移动托管线程。 |
从 Win32 线程处理到托管线程处理的映射
下表将 Win32 线程处理元素映射为其近似的运行时等效元素。 注意,此映射不表示具有相同的功能。 例如,TerminateThread 不执行 finally 子句或释放资源,并且不能被禁止。 但 Thread.Abort 可以执行所有回滚代码,回收所有资源,并可以使用 ResetAbort 来拒绝。 请确保在对功能进行假设之前仔细阅读该文档。
在 Win32 中 |
在公共语言运行时中 |
---|---|
CreateThread |
Thread 和 ThreadStart 的组合。 |
TerminateThread |
|
SuspendThread |
|
ResumeThread |
|
Sleep |
|
线程句柄上的 WaitForSingleObject |
|
ExitThread |
无等效项 |
GetCurrentThread |
|
SetThreadPriority |
|
无等效项 |
|
无等效项 |
|
接近 CoInitializeEx (OLE32.DLL) |
托管线程和 COM 单元
可以标记一个托管线程以指示它将承载一个单线程或多线程单元。 Thread 类的 GetApartmentState、SetApartmentState 和 TrySetApartmentState 方法返回并分配线程的单元状态。 如果未设置该状态,则 GetApartmentState 返回 ApartmentState.Unknown。
注意 |
---|
在 .NET Framework 1.0 和 1.1 版中,ApartmentState 属性用于获取和设置单元状态。 |
只有当线程处于 ThreadState.Unstarted 状态时才可以设置该属性;但一个线程只能设置一次。
如果在启动线程之前未设置单元状态,则该线程被初始化为多线程单元 (MTA)。 终结器线程和由 ThreadPool 控制的所有线程都是 MTA。
重要事项 |
---|
对于应用程序启动代码,控制单元状态的唯一方式是将 MTAThreadAttribute 或 STAThreadAttribute 应用于入口点过程。在 .NET Framework 1.0 和 1.1 版中,可将 ApartmentState 属性设置为第一行代码。此属性在 .NET Framework 2.0 版中是不允许的。 |
向 COM 公开的托管对象的行为就如同它们聚合了自由线程封送拆收器一样。 换句话说,它们可以通过自由线程的方式从任何 COM 单元中调用。 唯一不显示这种自由线程行为的托管对象是那些从 ServicedComponent 派生的对象。
在托管领域中,不支持 SynchronizationAttribute,除非使用上下文和上下文绑定的托管实例。 如果使用的是 EnterpriseServices,则对象必须从 ServicedComponent(它本身是从 ContextBoundObject 派生的)派生。
当托管代码调用至 COM 对象时,它总是遵循 COM 规则。 换句话说,它遵循 OLE32 的规定,通过 COM 单元代理和 COM+ 1.0 上下文包装来调用。
阻止问题
在阻止了非托管代码中的线程的操作系统中,如果线程进行非托管调用,则运行时将不会为 Thread.Interrupt 或 Thread.Abort 控制该线程。 对于 Thread.Abort,运行时将该线程标记为 Abort,并在重新进入托管代码时控制该线程。 使用托管阻止而不使用非托管阻止更为可取。 WaitHandle.WaitOne、WaitHandle.WaitAny、WaitHandle.WaitAll、Monitor.Enter、Monitor.TryEnter、Thread.Join、GC.WaitForPendingFinalizers 等都对 Thread.Interrupt 和 Thread.Abort 做出响应。 而且,如果您的线程处于单线程单元,则当您的线程被阻止时,单元中的所有托管阻止操作都将正确发送消息。