Windows线程同步--临界区对象(Critical Section Objects)
1.简介
用于防止共享资源被同时访问,从而保证多线程安全。事件(Event)、互斥体(Mutex)、信号量(Semaphore)可以被用于进程间并行编程,但是临界区只能用于一个进程的线程中,而且在某一时刻,一个临界区对象只能被一个线程拥有。这样做的优点是提高了性能!
旋转锁(Spin):在多核CPU中需要考虑个问题,临界资源可能正在被一个线程A使用,而同时另一个核中运行的线程B也想要这个资源,这时发现临界资源被占用,如果直接从运行状态转换成等待状态,这个转换需要消耗比如1000个CPU时钟,而还未转换完时,也许线程A已经退出了临界区,势必造成CPU资源的浪费。所以微软给临界区增加了Spin,是个次数,让线程可以多次尝试进入临界区,减少转换的频率。这个问题在单核CPU系统中不用考虑,因为同一时刻只能有一个线程处于运行状态。
2.数据结构和方法
CRITICAL_SECTION
在Windows系统中,当有一个资源(比如一个变量)要被多个线程使用时,需要声明一个CRITICAL_SECTION结构,用于存储临界区信息。
InitializeCriticalSection
用CRITICAL_SECTION结构初始化一个临界区对象。
InitializeCriticalSectionAndSpinCount
用CRITICAL_SECTION结构初始化一个临界区对象,并可以传入一个Spin count,这个值在单核系统中会被忽略,在多核系统中在尝试spin count次之后还是进不了临界区才转成等待状态。
DeleteCriticalSection
删除指定的临界区对象
EnterCriticalSection
进入临界区,相当于PV操作中的P操作。会先判断是否能进入,不能则等待,即堵塞住(block)
TryEnterCriticalSection
类似EnterCriticalSection,不同的时,如果无法进入,则立即返回,不堵塞住。返回值非零表示成功占有临界区;返回值为零表明临界区被其他线程占用。
LeaveCriticalSection
离开临界区,相当于PV操作中的V操作。
DeleteCriticalSection
释放掉被临界区占用的资源
3.常用封装
class CCritSec
{
CRITICAL_SECTION m_CritSec;
// make copy constructor and assignment operatorinaccessible
CCritSec(const CCritSec &refCritSec);
CCritSec &operator=(const CCritSec&refCritSec);
public:
CCritSec() { InitializeCriticalSection(&m_CritSec); }
~CCritSec() { DeleteCriticalSection(&m_CritSec); }
void Lock() { EnterCriticalSection(&m_CritSec);}
void Unlock() { LeaveCriticalSection(&m_CritSec);}
};
class CAutoLock
{
// make copy constructor and assignment operatorinaccessible
CAutoLock(const CAutoLock &refAutoLock);
CAutoLock &operator=(const CAutoLock&refAutoLock);
protected:
CCritSec * m_pLock;
public:
CAutoLock(CCritSec* plock)
{
m_pLock = plock;
m_pLock->Lock();
};
~CAutoLock() { m_pLock->Unlock(); }
};
这样大大简化了在多线程代码中对临界区对象的使用。
比如在类中声明一个成员变量:
CCritSec m_CritSec;
在访问公共变量(临街资源)的成员方法中加一句:
CAutoLock autoLock(&m_CritSec);
则相当于给这个的前后加了PV操作。从而实现互斥的访问临界资源
参考资料
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682530(v=vs.85).aspx