critical section(临界区)
在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区包含两个操作原语: EnterCriticalSection() 进入临界区 LeaveCriticalSection() 离开临界区。
优点:速度快。因为Critical Section不是内核对象,函数EnterCriticalSection()和LeaveCriticalSection()的调用一般都在用户模式内执行。只有当想要获得的锁正好被别的线程拥有时才会退化成和Mutex一样,即转换到内核模式。
缺点:只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
mutex(互斥器)
只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。
在这里,获得mutex的操作,就好比进入Critical Section,所以才会有文章将两者相比
定义写法 |
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); |
mutex一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。
优点:可以跨进程
缺点:速度慢。Mutex 是内核对象,相关函数的执行 (WaitForSingleObject,ReleaseMutex)需要用户模式(User Mode)到内核模式(Kernel Mode)的转换,在x86处理器上这种转化一般要发费600个左右的 CPU指令周期。
semaphore(信号量)
信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大数目N。对于N=1的情况,称为binary semaphore。
在有的系统中Binary semaphore与Mutex是没有差异的。在有的系统上,主要的差异是mutex一定要由获得锁的进程来释放。因此建议mutex只用于保护critical section(线程间互斥)。而semaphore则用于保护某变量,或者同步。
spin lock(自旋锁)
自旋锁是专为防止多处理器并发而引入的一种锁。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。