ATL对象多线程访问临界锁的实现

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;
};
</
_threadmodel>

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><threadmodel>* m_p;
};
</
threadmodel></class>

CComObjectLockT在构造函数和析构函数中分别调用了CComObjectRootEx的Lock和Unlock。因此,如果将CComObjectLockT对象实例分配在栈里的话,在栈被撤销的时候,CComObjectLockT将被自动析构,Unlock也就自动被调用。一个例子:

HRESULT ComObject::put_Name (BSTR bstrName)
{
    
ObjectLock objlock(this);
    ......
}

在上述例子中,Unlock将在put_Name方法执行完毕后,随着objlock对象的析构而自动执行。

Leave a comment

Please be polite and on topic. Your e-mail will never be published.