COM对象必须在套间中运行。套间分为单线程套间和多线程套间。在单线程套间中,套间保证COM对象实例仅有一个线程可以访问,而在多线程套间中, COM对象实例可同时被多个线程访问。因此,在多线程套间中执行的COM对象必须解决多线程访问的同步和冲突等线程安全相关问题。
引用计数器管理的实现——CComObjectRootEx
ATL使用CComObjectRootEx类来实现对COM对象计数器的管理,因此,所有的基于ATL的COM对象必须从该类继承。CComObjectRootEx类的声明和实现如下(精简):
template< class ThreadModel >
class CComObjectRootEx : public CComObjectRootBase
{
public:
typedef ThreadModel _ThreadModel;
...
ULONG InternalAddRef()
{ return _ThreadModel::Increment(&m_dwRef); } // 引用计数器m_dwRef在
// CComObjectRootBase中声明
ULONG InternalRelease()
{ return _ThreadModel::Decrement(&m_dwRef); }
};
由上述代码可见,对于CComObjectRootEx类:
1) 实现了对引用计数器递增与递减的操作,但并未实现IUnknown接口所要求的对对象声明周期的管理,没有在计数器归零后释放对象。
2) 对于应用技术的操作依赖于模板参数ThreadModel,即该对象的线程模型,包括CComSingleThreadModel类和CComMultiThreadModel类。因此,对象访问的线程安全问题,将在这两个类中实现。
套间线程模型的实现——CComSingleThreadModel与CComMultiThreadModel
CComSingleThreadModel类和CComMultiThreadModel类的实现概要如下:
class CComSingleThreadModel
{
typedef CComFakeCriticalSection AutoCritcalSection;
typedef CComFakeCriticalSection CriticalSection;
typedef CComSingleThreadMode ThreadModeNoCS;
static ULONG Increment(LPLONG p) { return ++(*p); }
static ULONG Decrement(LPLONG p) { return --(*p); }
};
class CComMultiThreadModel
{
typedef CComAutoCriticalSection AutoCritcalSection;
typedef CComCriticalSection CriticalSection;
typedef CComMultiThreadModeNoCs ThreadModeNoCS;
static ULONG Increment(LPLONG p)
{ return InterlockedIncrement(p); }
static ULONG Decrement(LPLONG p)
{ return InterlockedDecrement(p); }
};
CComSingleThreadModel与CComMultiThreadModel具有相同的结构和方法声明,只是在实现计数器递增或递减的 时候,CComMultiThreadModel采用了线程安全的API函数而CComSingleThreadModel。并且, CComMultiThreadModel声明了真实的临界区对象类型(CComAutoCriticalSection和 CComCriticalSection ),供其他需要线程同步的操作使用。CComSingleThreadModel仅仅为保持与CComMultiThreadModel具有相同的结构而 声明了两个伪装的临界区类型(CComFakeCriticalSection)。CComFakeCriticalSection是伪装的临界区类,其 中并没有提供真实的临界区,而仅仅声明了与CComAutoCriticalSection和CComCriticalSection相同的方法。
结论
为了对单线程套间对象与多线程套间对象的引用计数实现一个统一的访问控制模型,ATL将COM的引用计数器管理的实现从IUnknown接口所定义 的对象生命周期管理中抽出,并在CComObjectRootEx类中单独实现。CComObjectRootEx通过模板参数指定对象所使用的线程模 型。在CComMultiThreadModel中声明和实现了线程安全的方法。由此,任何ATL COM对象必须继承自CComObjectRootEx来实现引用计数,并在继承时指定线程模型。