Mutex和Critical Section都是主要用于限制多线程(Multithread)对全局或共享的变量、对象或内存空间的访问。下面是其主要的异同点(不同的地方用绿色表示)。
|
Mutex
|
Critical Section
|
性能和速度
|
慢。
Mutex
是内核对象,相关函数的
执
行
(
WaitForSingleObject
,
ReleaseMutex
)需要用户模式(
User Mode
)到内核模式(
Kernel Mode
)的转换,在
x86
处理器上这种转化一般要发费
600
个左右的
CPU
指令周期。
|
快。
Critical Section
本身不是内核对象,相关函数(
EnterCriticalSection
,
LeaveCriticalSection
)的调用一般都在用户模式内执行,在
x86
处理器上一般只需要发费
9
个左右的
CPU
指令周期。只有当想要获得的锁正好被别的线程拥有时才会退化成和
Mutex
一样,即转换到内核模式,发费
600
个左右的
CPU
指令周期。
|
能否跨越进程(
Process
)边界
|
可以
|
不可
|
定义写法
|
HANDLE
hmtx;
|
CRITICAL_SECTION
cs;
|
初始化写法
|
hmtx= CreateMutex (NULL, FALSE, NULL);
|
InitializeCriticalSection(&cs);
|
结束清除写法
|
CloseHandle(hmtx);
|
DeleteCriticalSection(&cs);
|
无限期等待的写法
|
WaitForSingleObject (hmtx, INFINITE);
|
EnterCriticalSection(&cs);
|
0等待(状态检测)的写法
|
WaitForSingleObject (hmtx, 0);
|
TryEnterCriticalSection(&cs);
|
任意时间等待的写法
|
WaitForSingleObject (hmtx, dwMilliseconds);
|
不支持
|
锁释放的写法
|
ReleaseMutex(hmtx);
|
LeaveCriticalSection(&cs);
|
能否被一道用于等待其他内核对象
|
可以(使用
WaitForMultipleObjects
,
WaitForMultipleObjects
Ex
,
Msg
WaitForMultipleObjects
,
Msg
WaitForMultipleObjects
Ex
等等
)
|
不可
|
当拥有锁的线程死亡时
|
Mutex
变成
abandoned
状态,其他的等待线程可以获得锁。
|
Critical Section
的状态不可知(
undefined
),以后的动作就不能保证了。
|
自己会不会锁住自己
|
不会(对已获得的Mutex,重复调用WaitForSingleObject不会锁住自己。但最后你别忘了要调用同样次数的ReleaseMutex)
|
不会(对已获得的Critical Section,重复调用EnterCriticalSection不会锁住自己。但最后你别忘了要调用同样次数的LeaveCriticalSection)
|
下面是一些补充:
l 请先检查你的设计,把不必要的全局或共享对象改为局部对象。全局的东西越少,出问题的可能就越小。
l 每次你使用EnterCriticalSection时,请不要忘了在函数的所有可能返回的地方都加上LeaveCriticalSection。对于Mutex也同样。若你把这个问题和Win32 structured exception或C++ exception一起考虑,你会发现问题并不是那么简单。自定义一个封装类可能是一种解决方案,以Critical Section为例的代码如下所示:
class csholder
{
CRITICAL_SECTION *cs;
public:
csholder(CRITICAL_SECTION *c): cs(c)
{ EnterCriticalSection(cs); }
~csholder() { LeaveCriticalSection(cs); }
};
CRITICAL_SECTION some_cs;
void f
oo
()
{
// ...
csholder hold_some(&some_cs);
// ... CS protected code here
// at return or if an exception happens
// hold_some's destructor is automatically called
}
l 根据你的互斥范围需求的不同,把Mutex或Critical Section定义为类的成员变量,或者静态类变量。
l 若你想限制访问的全局变量只有一个而且类型比较简单(比如是LONG或PVOID型),你也可以使用InterlockedXXX系列函数来保证一个线程写多个线程读。
本文主要参照了Jeffrey Richter的《Programming Applications for Microsoft Windows, 4
th Ed.》。