COM 对象的智能临界区包装器





2.00/5 (1投票)
2000 年 3 月 30 日

84911
一个非常简单的类,它包装了 win32 CRITICAL_SECTION。非常适合 COM STA 或 MTA。
引言
越来越多的 COM 对象被编写为“Both”线程,因为这使得在 COM STA 或 MTA 中创建对象成为可能。如果创建“Both”线程 COM 对象的线程通过使用 CoInitialize()
、CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)
或 OleInitialize()
初始化为位于 STA 中,则该对象将在同一 STA 中创建。如果线程改为通过使用 CoInitializeEx(NULL, COINIT_MULTITHREADED)
初始化为位于 MTA 中,则该对象将在进程的唯一 MTA 中创建。
因此,对象的开发人员被迫为多个线程的同时访问编写代码,通常通过使用临界区。请注意,任何全局和静态变量都需要受到保护。我在这里展示的是优化保护对象实例状态的代码。我还想指出,如果对象聚合了自由线程封送器 (FTM),则不应使用此技术。类 CComSmartAutoCriticalSection
是一个非常简单的类,它包装了一个 WIN32 CRITICAL_SECTION
。该类的定义如下
//////////////////////////////////////////////////////////////////////////////// // CComSmartAutoCriticalSection // class CComSmartAutoCriticalSection { public: CComSmartAutoCriticalSection() { if (SUCCEEDED(::CoInitialize(NULL))) { ::CoUninitialize(); m_bInMTA = FALSE; } else { m_bInMTA = TRUE; } if (m_bInMTA) ::InitializeCriticalSection(&m_csSafeAccess); } ~CComSmartAutoCriticalSection() { if (m_bInMTA) ::DeleteCriticalSection(&m_csSafeAccess); } void Lock() { if (m_bInMTA) ::EnterCriticalSection(&m_csSafeAccess); } void Unlock() { if (m_bInMTA) ::LeaveCriticalSection(&m_csSafeAccess); } private: BOOL m_bInMTA; CRITICAL_SECTION m_csSafeAccess; };
构造函数检测创建线程是否属于 COM STA 或 MTA,并初始化私有成员变量 m_bInMTA。CoInitialize()
调用如果调用线程属于 STA 将返回 S_FALSE,如果调用线程属于 MTA 将返回 RPC_E_CHANGED_MODE。如果对象正在 MTA 中创建,则初始化 m_csSafeAccess 临界区。
如果对象在 MTA 中创建,则析构函数会删除 m_csSafeAccess 临界区。
Lock()
和 Unlock()
方法分别调用 WIN32 EnterCirticalSection()
和 LeaveCriticalSection()
API,仅当对象在 MTA 中创建时才调用。
用法
在实现 COM 对象的 C++ 类中,定义一个类型为 CComSmartAutoCriticalSection
的成员变量。
class ATL_NO_VTABLE CSomeCOMClass : <inheritance list> { private: CComSmartAutoCriticalSection m_csSafeAccess; }
接口属性和方法的实现函数应包装在 Lock()
和 Unlock()
函数中。
STDMETHODIMP CSomeCOMClass::SomeMethod() { HRESULT hResult = S_OK; m_csSafeAccess.Lock(); ... // Code to do what the method does. m_csSafeAccess.Unlock(); return hResult; }