Now what about those COM apartments? How do these new .NET threads handle COM apartments? .NET threads can reside in both single and multithreaded apartments. When a .NET thread is first started it exists neither in a single-threaded or multithreaded apartment. A static state variable Thread.CurrentThread
. Apartment indicates the current apartment type. If you run the code in Listing 4, then the apartment type will be Unknown, as the thread would not have entered an apartment yet.
Listing 4: Threading Model Attributes
using System;
using System.Threading;
namespace ConsoleApplication5
{
class Class1
{
// line output
// // Unknown
// [STAThread] // STA
// [MTAThread] // MTA
public static void Main(String[] args)
{
Console.WriteLine("Apartment State = {0}",
Thread.CurrentThread.ApartmentState);
}
}
}
If you uncomment the line with the STAThread
attribute, then the thread set its ApartmentState to STA. If you uncomment the line with the MTAThread attribute, then the thread set its ApartmentState
to MTA
. This allows control over the apartment type, similar to CoInitializeEx
. You can also set the ApartmentState static member directly (see Listing 5).
Listing 5: ApartmentState
using System;
using System.Threading;
namespace ConsoleApplication6
{
class Class1
{
static void Main(string[] args)
{
// Thread.CurrentThread.ApartmentState =
// ApartmentState.STA;
Thread.CurrentThread.ApartmentState =
ApartmentState.MTA;
Console.WriteLine("Apartment State = {0}",
Thread.CurrentThread.ApartmentState);
}
}
}
Setting the ApartmentState
property has the same affect as using the STAThread
and MTAThread
attributes.
There are also class attributes that affect the threading model used by the .NET framework. The ThreadAffinity and Synchronization class attributes can be used to synchronize access to a class and its instance members.
[ThreadAffinity()] public class Class1 : ContextBoundObject
[Synchronization()] public class Class1 : ContextBoundObject
When calling into such classes, the calls are serialized to limit access to the class to one thread at any one time. At this point, these class context attributes are really thin on documentation. So, I'll save a more in-depth explanation that may be incorrect anyway.
Win32 to .NET
I figured with all this work I'm doing learning .NET threads that I would leave you with an important resource. Table 1 shows my attempt in converting Win32 functions to .NET classes and methods.
Table 1: Converting Win32 to .NET
Win32 | .NET |
---|---|
CreateEvent | new System.Threading.Event |
CreateMutex | new System.Threading.Mutex |
CreateSemaphore | n/a |
CreateThread | new System.Threading.Thread and new System.Threading.ThreadStart |
CreateWaitableTimer | new System.Threading.Timer |
InitializeCriticalSectiona EnterCriticalSection LeaveCriticalSection DeleteCriticalSection |
lock (C#) System.Threading.Monitor |
InterlockedCompareExchange | System.Threading.Interlock.CompareExchange |
InterlockedDecrement | System.Threading.Interlock.Decrement |
InterlockedExchange | System.Threading.Interlock.Exchange |
InterlockedIncrement | System.Threading.Interlock.Increment |
OpenEvent | n/a |
OpenMutex | new System.Threading.Mutex |
OpenSemaphore | n/a |
OpenWaitableTimer | n/a |
PulseEvent | n/a |
ReleaseMutex | System.Threading.Mutex.ReleaseMutex |
ReleaseSemaphore | n/a |
ResetEvent | System.Threading.AutoResetEvent.Reset or System.Threading.ManualResetEvent.Reset |
ResumeThread | System.Threading.Thread.Resume |
SetEvent | System.Threading.AutoResetEvent.Set or System.Threading.ManualResetEvent.Set |
SetWaitableTimer | n/a |
Sleep | System.Threading.Thread.Sleep |
SuspendThread | System.Threading.Thread.Suspend |
TerminateThread | System.Threading.Thread.Abort |
WaitForSingleObject and WaitForSingleObjectEx |
System.Threading.Thread.Join or System.Threading.Monitor.Wait or System.Threading.WaitHandle.WaitOne |
WaitForMultipleObjects and WaitForMultipleObjects |
System.Threading.WaitHandle.WaitAll or System.Threading.WaitHandle.WaitAny |
If you were to undertake a project of converting a Win32 application to a .NET application, then this table could prove very useful. In some cases, a few objects and methods in the .NET framework could closely emulate a Win32 function. I had to, on occasion, decide how closely they matched and sometimes decided that a match was not appropriate. As an example, you could create a semaphore with a Mutex object and a counter. But I wouldn't say it's a close match, so I didn't mention these instances. In other cases, I had to decide between two matches.
Comments