Managed and Unmanaged Threading in Windows
Updated: December 2009
Management of all threads is done through the Thread class, including threads created by the common language runtime and those created outside the runtime that enter the managed environment to execute code. The runtime monitors all the threads in its process that have ever executed code within the managed execution environment. It does not track any other threads. Threads can enter the managed execution environment through COM interop (because the runtime exposes managed objects as COM objects to the unmanaged world), the COM DllGetClassObject() function, and platform invoke.
When an unmanaged thread enters the runtime through, for example, a COM callable wrapper, the system checks the thread-local store of that thread to look for an internal managed Thread object. If one is found, the runtime is already aware of this thread. If it cannot find one, however, the runtime builds a new Thread object and installs it in the thread-local store of that thread.
In managed threading, Thread.GetHashCode is the stable managed thread identification. For the lifetime of your thread, it will not collide with the value from any other thread, regardless of the application domain from which you obtain this value.
An operating-system ThreadId has no fixed relationship to a managed thread, because an unmanaged host can control the relationship between managed and unmanaged threads. Specifically, a sophisticated host can use the Fiber API to schedule many managed threads against the same operating system thread, or to move a managed thread among different operating system threads.
The following table maps Win32 threading elements to their approximate runtime equivalent. Note that this mapping does not represent identical functionality. For example, TerminateThread does not execute finally clauses or free up resources, and cannot be prevented. However, Thread.Abort executes all your rollback code, reclaims all the resources, and can be denied using ResetAbort. Be sure to read the documentation closely before making assumptions about functionality.
In the common language runtime
Combination of Thread and ThreadStart
WaitForSingleObject on the thread handle
Close to CoInitializeEx (OLE32.DLL)
A managed thread can be marked to indicate that it will host a single-threaded or multithreaded apartment. The GetApartmentState, SetApartmentState, and TrySetApartmentState methods of the Thread class return and assign the apartment state of a thread. If the state has not been set, GetApartmentState returns ApartmentState.Unknown.
In the .NET Framework versions 1.0 and 1.1, the ApartmentState property is used to get and set the apartment state.
The property can be set only when the thread is in the ThreadState.Unstarted state; it can be set only once for a thread.
If the apartment state is not set before the thread is started, the thread is initialized as a multithreaded apartment (MTA). The finalizer thread and all threads controlled by ThreadPool are MTA.
For application startup code, the only way to control apartment state is to apply the MTAThreadAttribute or the STAThreadAttribute to the entry point procedure. In the .NET Framework versions 1.0 and 1.1, the ApartmentState property can be set as the first line of code. This is not permitted in the .NET Framework version 2.0.
Managed objects that are exposed to COM behave as if they had aggregated the free-threaded marshaler. In other words, they can be called from any COM apartment in a free-threaded manner. The only managed objects that do not exhibit this free-threaded behavior are those objects that derive from ServicedComponent.
In the managed world, there is no support for the SynchronizationAttribute unless you use contexts and context-bound managed instances. If you are using EnterpriseServices, then your object must derive from ServicedComponent (which is itself derived from ContextBoundObject).
When managed code calls out to COM objects, it always follows COM rules. In other words, it calls through COM apartment proxies and COM+ 1.0 context wrappers as dictated by OLE32.
If a thread makes an unmanaged call into the operating system that has blocked the thread in unmanaged code, the runtime will not take control of it for Thread.Interrupt or Thread.Abort. In the case of Thread.Abort, the runtime marks the thread for Abort and takes control of it when it re-enters managed code. It is preferable for you to use managed blocking rather than unmanaged blocking. WaitHandle.WaitOne,WaitHandle.WaitAny, WaitHandle.WaitAll, Monitor.Enter, Monitor.TryEnter, Thread.Join, GC.WaitForPendingFinalizers, and so on are all responsive to Thread.Interrupt and to Thread.Abort. Also, if your thread is in a single-threaded apartment, all these managed blocking operations will correctly pump messages in your apartment while your thread is blocked.