Win32多线程之互斥器(Mutexes)

http://blog.csdn.net/wjj715211/article/details/16899667

Win32的Mutex用途和critical section非常类似,但是它牺牲速度以增加弹性,或许你已经猜到了,mutex 是MUTual EXclusion的缩写。一个时间内只能够有一个线程拥有mutex,就好像同一时间内只能够有一个线程进入同一个critical section一样。

  虽然mutex和critical section做同样的事情,但是它们的运作还是有差别的:

  1)锁住一个未被拥有的mutex, 比锁住一个未被拥有的critical section,需要花费几乎100倍价的时间。因为critical section不需要进入操作系统核心,直接在“user mode”就可以进行操作。

  2)Mutexes可以跨进程使用。Critical section只能在同一个进程中使用。

  3)等待一个mutex时,你可以指定“结束等待”的时间长度。但对于critical section则不行。

    以下是两种对象的相关函数比较:

  CRITICAL_SECTION                               Mutex 核心对象


 InitializeCriticalSection()                         CreateMutex()

                                                                      OpenMutex()

                                                                      WaitForSingleObject()

 EnterCriticalSection()                         WaitForMultipleObjects()

                                                                      MsgWaitForMultipleObjects()

LeaveCriticalSection()                            ReleaseMutex()

DeleteCriticalSection()                      CloseHandle()


  为了能够跨进程使用同一个mutex,你可以在产生mutex时指定其名称,如果你指定了名称,系统中的其他任何线程就可以使用这个名称来处理该mutex。一定要使用名称,因为你没有办法把handle交给一个执行中的进程。

   记住,其他程序也可能使用这个同步机制,所以mutex名称对整个系统而言是全局性的。不要把你的mutex对象命名为“Object”或“Mutex”之类,那太过普遍。请使用一些独一无二的名称,如公司名称或应用程序名称等。

  产生一个互斥器(Mutex)

   与critical sections不同,当你产生一个mutex时,你有某些选择空间。Mutex是一个核心对象,因此它被保持在系统核心之中,并且和其他核心对象一样,有所谓的引用计数(reference count)。虽然mutex的机能与critical section十分类似,但由于Win32术语带来的迷惑,mutex可能不太容易了解,你可以利用CreateMutex()产生一个mutex:

HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
BOOL bInitialOwner, // 初始化互斥对象的所有者,如果你希望“调用CreateMutex()的这个线程”拥有产生出来的mutex,就将该值设为TRUE。
LPCTSTR lpName // mutex的名称(一个字符串)。任何进程或线程都可以根据此名称使用这一mutex。名称可以是任何字符串,只要不 含反斜线(\)即可。
);
返回值:
       如果成功,则传回一个handle,否则传回NULL。调用GetLastError()可以获得更进一步的信息。如果指定的mutex名称已经存在,GetLastError()会传回ERROR_ALREADY_EXISTS。

当你不再需要一个mutex时,你可以调用CloseHandle()将它关闭。和其他核心对象一样,mutex有一个引用计数(reference count)。每次你调用CloseHandle(),引用计数便减1,当引用计数达到0时,mutex便自动被系统清除掉。下面这个CreateAndDeleteMutex()函数会产生一个mutex,然后把它删除,这个mutex没有安全属性,不属于现行线程,名为“Demonstration Mutex”。
HANDLE   hMutex;
void  CreateAndDeleteMutex()
{
  hMutex = CreateMutex(NULL, FALSE, " Demonstration Mutex");
  /*  Do something here  */
  CloseHandle(hMutex);
}

打开一个互斥器(Mutex)

             如果mutex已经被产生了,并有一个名称,那么任何其他的进程和线程便可以根据该名称打开那个mutex(我这里并不考虑安全属性)。

             如果你调用CreateMutex()并指定一个早已存在的mutex名称。Win32会传回给你一个mutex handle,而不会为你产生一个新的mutex。就像上面所说的,GetLastError()会传回ERROR_ALREADY_EXISTS。

            你也可以使用OpenMutex()打开(而非产生)一个原已存在的mutex。这种情况通常是因为,你写了一个client进程,并与同一台机器上地server进程交谈,而只有server进程才应该产生mutex,因为它保护了server所定义的结构体。

         锁住一个互斥器(Mutex)

             欲获得一个mutex的拥有权,请使用Win32的Wait...()函数。Wait...()对mutex所做的事情和EnterCriticalSection()对critical section所做的事情差不多,倒是一大堆术语容易让人迷惑。

       一旦没有任何线程拥有mutex,这个mutex便处于激发状态,因此,如果没有任何线程拥有那个mutex,Wait...()便会成功。反过来说,当线程拥有mutex时,它便不处于激发状态,如果有某个线程正在等待一个未被激发的mutex,它便将进入“blocking”(阻塞)状态。也就是说,该线程会停止执行,直到mutex被其拥有者释放并处于激发状态。

      下面是某种情节的发展:

      1.我们有一个mutex,此时没有任何线程拥有它;

      2.某个线程调用WaitForSingleObject()(或任何其他的Wait...()函数,并指定该mutex handle为参数;

      3.Win32于是将该mutex的拥有权给予这个线程,然后将此mutex的状态短暂地设为激发状态,于是Wait...()函数返回。

      4.Mutex立刻又被设定为非激发状态,使任何处于等待下的其他线程没有办法获得其拥有权。

      5.获得该mutex之线程调用ReleaseMutex(),将mutex释放掉。于是循环回到第一场景,周二复始。


ReleaseMutex函数:

 BOOL   ReleaseMutex(

         HANDLE   hMutex;

);

参数


hMutex            欲释放的mutex handle。


返回值

   如果成功,传回TTRUE,否则传回FALSE。


   Mutex的拥有权是第二个容易引人迷惑的地方。Mutex的拥有权并非属于哪个产生它的线程,而是那个最后对此mutex进行Wait...()操作并且尚未进行ReleaseMutex()操作的线程。线程拥有mutex就好像线程进入critical section一样,一次只能有一个线程拥有该mutex。

   Mutex的被摧毁和其他拥有权没什么关系。和大部分其他的核心对象一样,mutex是在其引用计数降为0时被操作系统摧毁的。每当线程对此mutex调用一次CloseHandle(),或是当线程结束时,mutex的引用计数即下降1。

   如果拥有某mutex之线程结束了,该mutex只有在一种情况下才会被自动清除:该线程是最后一个与该mutex handle有关联的线程。否则,此核心对象的引用计数仍然大于0,其他线程(以及进程)仍然可以拥有此mutex的合法handle。然而,当线程结束而没有释放某个mutex时,有一种特殊特殊的处理方式。


处理被舍弃的互斥器(Mutexes)

    在一个适当的程序中,线程绝对不应该在它即将结束前还拥有mutex,因为这意味着线程没有能够适当地清除其资源。不幸的是,我们并不身处在一个完美的世界,有时候,因为某种理由,线程可能没有在结束前调用ReleaseMutex()。为了解决这个问题,mutex有一个非常重要的特性,这性质在各种同步机制中是独一无二的。如果线程拥有一个mutex而在结束前没有调用ReleaseMutex(),mutex不会被摧毁。取而代之的是,该mutex会被视为“未被拥有”以及“未被激发”,而下一个等待中的线程会被以WAIT_ABANDONED_0通知。不论线程是因为ExitThread()而结束,或是因当掉而结束,这种情况都存在。

   如果其他线程正以WaitForMultipleObjects()等待此mutex,该函数也会返回,传回值介于WAIT_ABANDONED_0和(WAIT_ABANDONED_0_n+1)之间,其中的n是指handle数组的元素个数。线程可以根据这个值了解到究竟哪一个mutex被放弃了,至于WaitForSingleObje(),那只是传回WAIT_ABANDONED_0.

   "知道一个mutex被舍弃"很容易,但要知道怎样应对就比较难了。毕竟mutex是用来确保某些操作能够被自动进行的。如果线程死于半途,很有可能被保护的那些数据会受到无法修复的伤害。

  

   为什么有一个最初拥有者?

   CreateMutex()的第二个参数bInitialOwner,允许你指定现行线程(current thread)是否立刻拥有即将产生出来的mutex,乍见之下这个参数或许只是提供一种方便性,但事实上它阻止了一种race condition的发生。

    与critical section不同,mutex可以跨进程使用,以及跨线程使用。

Mutex可以根据其名称而被开启。所以,另一个进程可以完全不需要和产生mutex 的进程打招呼,就根据名称开启一个mutex,如果没有bInitialOwner,你就必须先创建一个Mutex(使用CreateMutex()),然后才能使用,如下代码:

  HANDLE hMutex = CreateMutex(NULL, FALSE, "Sample Name");

  int  result   = WaitForSingleObject(hMutex, INFINITE);

 

    但是这样的安排可能会产生race condition。如果在CreateMutex完成之后,发生了context switch,执行权被切换到另一个线程,那么其他进程就有可能在mutex的产生者调用WaitForSingleObject()之前,锁住这个mutex对象。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值