临界区,互斥量,信号量,事件的区别

2015年11月12日 wanglinqiang整理html

四种进程或线程同步互斥的控制方法

  1. 临界区: 经过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
  2. 互斥量: 为协调共同对一个共享资源的单独访问而设计的。
  3. 信号量: 为控制一个具备有限数量用户资源而设计。
  4. 事 件: 用来通知线程有一些事件已发生,从而启动后继任务的开始。

临界区(Critical Section)

保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只容许一个线程对共享资源进行访问。若是有多个线程试图同时访问临界区,那么在有一个线程进入后其余全部试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其余线程能够继续抢占,并以此达到用原子方式操 做共享资源的目的。数据库

临界区包含两个操做原语:数组

EnterCriticalSection() // 进入临界区
LeaveCriticalSection() // 离开临界区

EnterCriticalSection()语句执行后代码将进入临界区之后不管发生什么,必须确保与之匹配的 LeaveCriticalSection()都可以被执行到。不然临界区保护的共享资源将永远不会被释放。虽然临界区同步速度很快,但却只能用来同步本 进程内的线程,而不可用来同步多个进程中的线程。安全

MFC提供了不少功能完备的类,我用MFC实现了临界区。MFC为临界区提供有一个 CCriticalSection类,使用该类进行线程同步处理是 很是简单的。只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片断便可。Lock()后代 码用到的资源自动被视为临界区内的资源被保护。UnLock后别的线程才能访问这些资源。多线程

互斥量(Mutex)

互斥量跟临界区很类似,只有拥有互斥对象的线程才具备访问资源的权限,因为互斥对象只有一个,所以就决定了任何状况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其余线程在得到后得以访问资源。互斥量比临界区复杂。由于使用互斥不只仅可以在同一应用程序不一样线程中实现资源的安全共享,并且能够在不一样应用程序的线程之间实现对资源的安全共享。
互斥量包含的几个操做原语:并发

CreateMutex()   // 建立一个互斥量
OpenMutex()     // 打开一个互斥量
ReleaseMutex()  // 释放互斥量
WaitForMultipleObjects()    // 等待互斥量对象

一样MFC为互斥量提供有一个CMutex类。使用CMutex类实现互斥量操做很是简单,可是要特别注意对CMutex的构造函数的调用函数

CMutex( BOOL bInitiallyOwn = FALSE, LPCTSTR lpszName = NULL, LPSECURITY_ATTRIBUTES lpsaAttribute = NULL)

不用的参数不能乱填,乱填会出现一些意想不到的运行结果。操作系统

信号量(Semaphores)

信号量对象对线程的同步方式与前面几种方法不一样,信号容许多个线程同时使用共享资源 ,这与操做系统中的PV操做相同。它指出了同时访问共享 资源的线程 最大数目。它容许多个线程在同一时刻访问同一资源,可是须要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()建立信号量 时即要同时指出容许的最大资源计数和当前可用资源计数。通常是将当前可用资源计数设置为最大资源计数,每增长一个线程对共享资源的访问,当前可用资源计数 就会减1,只要当前可用资源计数是大于0的,就能够发出信号量信号。可是当前可用计数减少到0时则说明当前占用资源的线程数已经达到了所容许的最大数目, 不能在容许其余线程的进入,此时的信号量信号将没法发出。线程在处理完共享资源后,应在离开的同时经过ReleaseSemaphore()函数将当前可 用资源计数加1。在任什么时候候当前可用资源计数决不可能大于最大资源计数。线程

PV操做及信号量的概念都是由荷兰科学家E.W.Dijkstra提出的。信号量S是一个整数,S大于等于零时表明可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用共享资源的进程数。
P操做 申请资源:设计

(1) S减1;

(2) 若S减1后仍大于等于零,则进程继续执行;

(3) 若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,而后转入进程调度。
V操做 释放资源:

(1) S加1;

(2) 若相加结果大于零,则进程继续执行;

(3) 若相加结果小于等于零,则从该信号的等待队列中唤醒一个等待进程,而后再返回原进程继续执行或转入进程调度。

信号量包含的几个操做原语:

CreateSemaphore()   // 建立一个信号量
OpenSemaphore()     // 打开一个信号量
ReleaseSemaphore()  // 释放信号量
WaitForSingleObject()   // 等待信号量

事件(Event)

事件对象也能够经过通知操做的方式来保持线程的同步。而且能够实现不一样进程中的线程同步操做。
信号量包含的几个操做原语:

CreateEvent()   // 建立一个事件
OpenEvent()     // 打开一个事件
SetEvent()      // 回置事件
WaitForSingleObject()       // 等待一个事件
WaitForMultipleObjects()    // 等待多个事件

WaitForMultipleObjects 函数原型:

WaitForMultipleObjects(
    IN DWORD nCount,            // 等待句柄数
    IN CONST HANDLE *lpHandles, // 指向句柄数组
    IN BOOL bWaitAll,           // 是否彻底等待标志
    IN DWORD dwMilliseconds     // 等待时间
)

参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核对象的两种等待方式进行了指定,为TRUE时当全部对象都被通知时函数才会返回,为FALSE则只要其中任何一个获得通知就能够返回。 dwMilliseconds在这里的做用与在WaitForSingleObject()中的做用是彻底一致的。若是等待超时,函数将返回 WAIT_TIMEOUT。

总结

  1. 互斥量与临界区的做用很是类似,但互斥量是能够命名的,也就是说它能够跨越进程使用。因此建立互斥量须要的资源更多,因此若是只为了在进程内部是用的话使用临界区会带来速度上的优点并可以减小资源占用量 。由于互斥量是跨进程的互斥量一旦被建立,就能够经过名字打开它。
  2. 互斥量(Mutex),信号灯(Semaphore),事件(Event)均可以被跨越进程使用来进行同步数据操做,而其余的对象与数据同步操做无关,但对于进程和线程来说,若是进程和线程在运行状态则为无信号状态,在退出后为有信号状态。因此可使用WaitForSingleObject来等待进程和 线程退出。
  3. 经过互斥量能够指定资源被独占的方式使用,但若是有下面一种状况经过互斥量就没法处理,好比如今一位用户购买了一份三个并发访问许可的数据库系统,能够根据用户购买的访问许可数量来决定有多少个线程/进程能同时进行数据库操做,这时候若是利用互斥量就没有办法完成这个要求,信号灯对象能够说是一种资源计数器。

关于更详细的一篇介绍,请看这里:
http://www.cppblog.com/killsound/archive/2009/07/15/16147.html

相关文章
相关标签/搜索