The threading-model class determines how your object will be reference counted and which critical-section class will be used to protect key areas from concurrent access if necessary. This choice is initially made in the Object Wizard, as we describe in Chapter 4, but can be modified post-wizard at any time. An ATL COM object specifies its threading model as a template parameter to the CComObjectRootEx class. Each of the threading-model classes implements an Increment and Decrement function and declares a typedef for the critical-section class appropriate for the threading model. Table 6-1 lists ATL's threading-model classes, their methods of reference counting, and their associated critical-section classes.
Table 6-1. The ATL Threading-Model Classes.
Class | Increment/Decrement | Critical Section |
---|---|---|
CComSingleThreadModel | Uses C++ operators ++ and -- | CComFakeCriticalSection |
CComMultiThreadModel | InterlockedIncrement/Decrement | CComAutoCriticalSection |
CComMultiThreadModelNoCS | InterlockedIncrement/Decrement | CComFakeCriticalSection |
If an object is created for the single-threading or apartment-threading model, CComSingleThreadModel is used. The Both and Free threading models use CComMultiThreadModel. CComMultiThreadModelNoCS is a hybrid of the two threading models, for cases in which an object can be accessed by multiple threads but doesn't require synchronization in its methods. In the following code, notice that the methods of the threading-model classes are static:
class CComSingleThreadModel { public: static ULONG WINAPI Increment(LPLONG p) {return ++(*p);} static ULONG WINAPI Decrement(LPLONG p) {return --(*p);} typedef CComFakeCriticalSection AutoCriticalSection; typedef CComFakeCriticalSection CriticalSection; typedef CComSingleThreadModel ThreadModelNoCS; }; |
Static members allow CComObjectRootEx to utilize these methods for reference counting without ever actually instantiating a threading-model class.
ATL defines two additional classes for threading, CComObjectThreadModel and CComGlobalsThreadModel. These are not new classes but typedefs that are defined based on one of the following threading preprocessors:
#if defined(_ATL_SINGLE_THREADED) typedef CComSingleThreadModel CComObjectThreadModel; typedef CComSingleThreadModel CComGlobalsThreadModel; #elif defined(_ATL_APARTMENT_THREADED) typedef CComSingleThreadModel CComObjectThreadModel; typedef CComMultiThreadModel CComGlobalsThreadModel; #else typedef CComMultiThreadModel CComObjectThreadModel; typedef CComMultiThreadModel CComGlobalsThreadModel; #endif |
By default, an ATL server will have the _ATL_APARTMENT_THREADED preprocessor defined in stdafx.h. You can use CComObjectThreadModel as your object's threading-model class, allowing the change from single-thread to multithread access by changing the preprocessor to _ATL_FREE_THREADED. Following this path forces the same threading model on all objects in a server that use CComObjectThreadModel. ATL uses CComGlobalsThreadModel internally to protect the module lock count and class object reference count.
Let's now look at how the threading-model class is used in an ATL COM object implementation through the CComObjectRoot classes.