Managed and Unmanaged Threading in Microsoft Windows
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.
Note 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.
Mapping from Win32 Threading to Managed Threading
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 Win32 | In the common language runtime |
---|---|
CreateThread | Combination of Thread and ThreadStart |
TerminateThread | Thread.Abort |
SuspendThread | Thread.Suspend |
ResumeThread | Thread.Resume |
Sleep | Thread.Sleep |
WaitForSingleObject on the thread handle | Thread.Join |
ExitThread | No equivalent |
GetCurrentThread | Thread.CurrentThread |
SetThreadPriority | Thread.Priority |
No equivalent | Thread.Name |
No equivalent | Thread.IsBackground |
Close to CoInitializeEx (OLE32.DLL) | Thread.ApartmentState |
Managed Threads and COM Apartments
A managed thread can be marked to indicate that it will host a single-threaded or multithreaded apartment. The Thread.ApartmentState property returns and assigns the apartment state of a thread. If the property has not been set, the property returns ApartmentState.Unknown.
The property can be set when the thread is in the ThreadState.Unstarted or ThreadState.Running state; however, it can be only be set once for a thread. The two valid property states are single-threaded apartment (STA) or multithreaded apartment (MTA). Either state has little effect on the managed portion of your application. The finalizer thread and all threads controlled by ThreadPool are MTA.
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.
Blocking Issues
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, WaitAny, WaitAll, Monitor.Enter, Monitor.Block, 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.
See Also
Thread.ApartmentState | Threadstate enumeration | ServicedComponent | Thread | Monitor