1. 几个临界区类
ATL将Windows临界区封装了一下,即CComCriticalSection和CComAutoCriticalSection类。两者的实现如下(精简):
class CComCriticalSection
{
public:
CComCriticalSection()
{ memset(&m_sec, 0, sizeof(CRITICAL_SECTION)); }
HRESULT Lock()
{ EnterCriticalSection(&m_sec); return S_OK; }
HRESULT Unlock()
{ LeaveCriticalSection(&m_sec); return S_OK; }
HRESULT Init()
{
InitializeCriticalSection(&m_sec);
return S_OK;
}
HRESULT Term()
{ DeleteCriticalSection(&m_sec); return S_OK; }
CRITICAL_SECTION m_sec;
};
class CComAutoCriticalSection : public CComCriticalSection
{
public:
CComAutoCriticalSection()
{ HRESULT hr = CComCriticalSection::Init(); }
~CComAutoCriticalSection()
{ CComCriticalSection::Term(); }
private:
HRESULT Init();
HRESULT Term();
};
CComCriticalSection类封装了一个临界区,以及对该临界区的初始化、锁定、解锁和释放操作。CComAutoCriticalSection是CComCriticalSection的一个子类,它实现了对临界区的自动初始化和释放。 CComAutoCriticalSection在构造和析构方法中嵌套了对临界去的初始化和释放操作,同时继承了锁定和解锁方法,并且把初始化和释放函数作了隐藏。
2. CComObjectRootEx类中的有关细节
CComObjectRootEx实现了线程安全的引用计数器,同时也声明了用于多线程同步访问对象的临界区和相关操作。CComObjectRootEx相关实现代码如下:
template < class ThreadModel >
class CComObjectRootEx : public CComObjectRootBase
{
public:
typedef ThreadModel _ThreadModel;
typedef _ThreadModel::AutoCriticalSection _CritSec;
typedef CComObjectLockT<_ThreadModel> ObjectLock;
... // 其他方法
void Lock() { m_critsec.Lock(); }
void Unlock() { m_critsec.Unlock(); }
private:
_CritSec m_critsec;
};
CComObjectRootEx提供了Lock和Unlock函数用来操作对象中的临界区,从而在多线程套间中,线程在访问互斥资源前,需要调用Lock来锁定临界区,在访问完毕后调用Unlock释放临界区,实现对象访问的线程安全性。当然,m_critsec在单线程套间中,将是一个伪临界区对象。
CComObjectRootEx中的Lock和Unlock函数必须成对使用。因而在一个分支较多的代码段中,必须确保每个分支在调用了Lock后都会调用Unlock。可是,疏忽有时候是难错免的,因此需要使用更安全的方法来实现Lock与Unlock的成对执行。这就是CComObjectLockT。
3. CComObjectLockT
首先看一下CComObjectLockT的实现。
template <class ThreadModel>
class CComObjectLockT
{
public:
CComObjectLockT(CComObjectRootEx<ThreadModel>* p)
{
if (p) p->Lock();
m_p = p;
}
~CComObjectLockT()
{ if (m_p) m_p->Unlock(); }
CComObjectRootEx<ThreadModel>* m_p;
};
CComObjectLockT在构造函数和析构函数中分别调用了CComObjectRootEx的Lock和Unlock。因此,如果将CComObjectLockT对象实例分配在栈里的话,在栈被撤销的时候,CComObjectLockT将被自动析构,Unlock也就自动被调用。一个例子:
HRESULT ComObject::put_Name (BSTR bstrName)
{
ObjectLock objlock(this);
......
}
在上述例子中,Unlock将在put_Name方法执行完毕后,随着objlock对象的析构而自动执行。