临界区 是应用程序互斥多线程的常用锁。 通常分4个步骤: 初始化( InitializeCriticalSection),进入临界区(EnterCriticalSection),离开(LeaveCritclSection).,释放(DeleteCriticalSection).
隐患:
1. InitializeCriticalSection 可能会因为内存不足而失败。
2. 由于可能会延迟初始化 ,系统可能会在程序调用EnterCritcalSection 时,进行初始化,所以EnterCriticalSection 也有可能会调用失败。
3. 如果一个线程调用了EnterCritiaclSection ,另外一个线程误调了LeaveCriticalSection ,可能会是个严重的误会。
4. 如果一个线程deleteCriticalSection ,而其他线程刚好又调用EnterCritcalSection,也会出现不测。
应对:
对于隐患1,2, 使用 InitializeCriticalSectionAndSpinCount 来初始化, 通过返回值判断是否成功分配内存。这样EnterCirticalSection不会有失败的情况。
对应隐患3,4, 主要是由于离散的误操作导致。为避免这种情况,可以对这些操作进行包装:
class TCritical{
LPCRITICAL_SECTION m_lc;
public:
static LPCRITICAL_SECTION CreateCritical()
{
LPCRITICAL_SECTION plc= new CRITICAL_SECTION;
if(FALSE == InitializeCriticalSectionAndSpinCount(plc,0x80000000))
return NULL;
else
{
return plc ;
}
}
static bool DeleteCritical(LPCRITICAL_SECTION & plc)
{
if(plc)
{
if(plc->LockCount==-1) //没有人使用了
{
DeleteCriticalSection(plc);
}
else //如果有人使用中,不可以删除,返回个错误,
return false ;
delete plc ;
}
return true;
}
TCritical(LPCRITICAL_SECTION lc)
{
m_lc = lc ;
}
virtual ~ TCritical()
{
if(m_lc)
{
DWORD threadId = (DWORD)m_lc->OwningThread;
if(threadId>0) //忘记调用LeaveCriticalSection
{
LeaveCriticalSection(m_lc);
}
}
}
//加锁
bool Lock()
{
if(m_lc ==NULL)
return false ;
if( m_lc->LockCount== 0)// 已经被Delete了
return false;
EnterCriticalSection(m_lc);
return true;
}
//解锁
bool UnLock()
{
if( m_lc==NULL)
return false ;
DWORD threadId = (DWORD)m_lc->OwningThread;
if( threadId == ::GetCurrentThreadId()) // 必须同样线程才可以解锁
{
LeaveCriticalSection(m_lc);
return true ;
}
return false ;
}
};
TCritical 类可以保证我们使用临界区基本是安全的了,并且也足够灵活,如果lock 和unLock 不在同一个函数里它也可以工作。如果想咬在同一个函数里工作自动加锁解锁的话,我们需要新增一个类:
class AutoCritical
{
bool m_bResult ;
TCritical* m_ptc;
public:
AutoCritical(TCritical *ptc)
:m_bResult(false)
{
m_ptc = ptc ;
if(m_ptc)
{
m_bResult = m_ptc->Lock();
}
}
// 我们需要时, 可以获取到调用的结果
bool GetResult(){ return m_bResult;}
~AutoCritical()
{
if(m_ptc)
m_bResult = m_ptc->UnLock();
}
};
AutoCritical 在栈上声明的对象可以保证在其生命周期结束时自动释放锁。很多时候我们希望可以在函数中尽快释放,所以需要控制其周期:
#define Begin_AutoCritical(a,cs) {AutoCritical a(cs);
#define End_AutoCritical };
使用如下:
LPCRITICAL_SECTION plc = TCritical::CreateCritical();
TCritical* pa = new TCritical(plc);
Begin_AutoCritical(autolock,pa);
....
End_AutoCritical; // 这里autolock 就被释放了