互斥内核对象

 

互斥对象(m u t e x)内核对象能够确保线程拥有对单个资源的互斥访问权。实际上互斥对象是因此而得名的。互斥对象包含一个使用数量,一个线程I D和一个递归计数器。互斥对象的行为特性与关键代码段相同,但是互斥对象属于内核对象,而关键代码段则属于用户方式对象。这意味着互斥对象的运行速度比关 键代码段要慢。但是这也意味着不同进程中的多个线程能够访问单个互斥对象,并且这意味着线程在等待访问资源时可以设定一个超时值。

I D用于标识系统中的哪个线程当前拥有互斥对象,递归计数器用于指明该线程拥有互斥对

象的次数。互斥对象有许多用途,属于最常用的内核对象之一。通常来说,它们用于保护由多个线程访问的内存块。如果多个线程要同时访问内存块,内存块中的数据就可能遭到破坏。互斥对象能够保证访问内存块的任何线程拥有对该内存块的独占访问权,这样就能够保证数据的完整性。

互斥对象的使用规则如下:

• 如果线程I D是0(这是个无效I D),互斥对象不被任何线程所拥有,并且发出该互斥对象的通知信号。

• 如果I D是个非0数字,那么一个线程就拥有互斥对象,并且不发出该互斥对象的通知信号。

• 与所有其他内核对象不同, 互斥对象在操作系统中拥有特殊的代码,允许它们违反正常的规则(后面将要介绍这个异常情况)。

若要使用互斥对象,必须有一个进程首先调用C r e a t e M u t e x,以便创建互斥对象:

HANDLE CreateMutex(
   PSECURITY_ATTRIBUTES psa,
   BOOL fInitialOwner,
   PCTSTR pszName);
p s a和p s z N a m e参数在第3章中做过介绍。当然,通过调用O p e n M u t e x,另一个进程可以获得它自己进程与现有互斥对象相关的句柄:

HANDLE OpenMutex(
   DWORD fdwAccess,
   BOOL bInheritHandle,
   PCTSTR pszName);
f I n i t i a l O w n e r参数用于控制互斥对象的初始状态。如果传递FA L S E(这是通常情况下传递的值),那么互斥对象的I D和递归计数器均被设置为0。这意味着该互斥对象没有被任何线程所拥有,因此要发出它的通知信号。

如果为f I n i t i a l O w n e r参数传递T R U E,那么该对象的线程I D被设置为调用线程的I D,递归计数器被设置为1。由于I D是个非0数字,因此该互斥对象开始时不发出通知信号。

通过调用一个等待函数, 并传递负责保护资源的互斥对象的句柄,线程就能够获得对共享资源的访问权。在内部,等待函数要检查线程的I D,以了解它是否是0(互斥对象发出通知信号)。如果线程I D是0,那么该线程I D被设置为调用线程的I D,递归计数器被设置为1,同时,调用线程保持可调度状态。

如果等待函数发现I D不是0(不发出互斥对象的通知信号),那么调用线程便进入等待状态。系统将记住这个情况,并且在互斥对象的I D重新设置为0时,将线程I D设置为等待线程的I D,将递归计数器设置为1,并且允许等待线程再次成为可调度线程。与所有情况一样,对互斥内核对象进行的检查和修改都是以原子操作方式进行的。

对于互斥对象来说,正常 的内核对象的已通知和未通知规则存在一个特殊的异常情况。比如说,一个线程试图等待一个未通知的互斥对象。在这种情况下,该线程通常被置于等待状态。然 而,系统要查看试图获取互斥对象的线程的I D是否与互斥对象中记录的线程I D相同。如果两个线程I D相同,即使互斥对象处于未通知状态,系统也允许该线程保持可调度状态。我们不认为该“异常”行为特性适用于系统中的任何地方的其他内核对象。每当线程成 功地等待互斥对象时,该对象的递归计数器就递增。若要使递归计数器的值大于1,唯一的方法是线程多次等待相同的互斥对象,以便利用这个异常规则。

一旦线程成功地等待到一 个互斥对象,该线程就知道它已经拥有对受保护资源的独占访问权。试图访问该资源的任何其他线程(通过等待相同的互斥对象)均被置于等待状态中。当目前拥有 对资源的访问权的线程不再需要它的访问权时,它必须调用R e l e a s e M u t e x函数来释放该互斥对象:

BOOL ReleaseMutex(HANDLE hMutex);
该函数将对象的递归计数器递减1。如果线程多次成功地等待一个互斥对象,在互斥对象的递归计数器变成0之前,该线程必须以同样的次数调用R e l e a s e M u t e x函数。当递归计数器到达0时,该线程I D也被置为0,同时该对象变为已通知状态。

当该对象变为已通知状态 时,系统要查看是否有任何线程正在等待互斥对象。如果有,系统将“按公平原则”选定等待线程中的一个,为它赋予互斥对象的所有权。当然,这意味着线程I D被设置为选定的线程的I D,并且递归计数器被置为1。如果没有其他线程正在等待互斥对象,那么该互斥对象将保持已通知状态,这样,等待互斥对象的下一个线程就立即可以得到互斥对 象。

9.6.1 释放问题

互斥对象不同于所有其他 内核对象,因为互斥对象有一个“线程所有权”的概念。本章介绍的其他内核对象中,没有一种对象能够记住哪个线程成功地等待到该对象,只有互斥对象能够对此 保持跟踪。互斥对象的线程所有权概念是互斥对象为什么会拥有特殊异常规则的原因,这个异常规则使得线程能够获取该互斥对象,尽管它没有发出通知。

这个异常规则不仅适用于 试图获取互斥对象的线程,而且适用于试图释放互斥对象的线程。当一个线程调用R e l e a s e M u t e x函数时,该函数要查看调用线程的I D是否与互斥对象中的线程I D相匹配。如果两个I D相匹配,递归计数器就会像前面介绍的那样递减。如果两个线程的I D不匹配,那么R e l e a s e M u t e x函数将不进行任何操作,而是将FA L S E(表示失败)返回给调用者。此时调用G e t L a s t E r r o r,将返回E R R O R _ N O T _ O W N E R(试图释放不是调用者拥有的互斥对象)。

因此,如果在释放互斥对 象之前,拥有互斥对象的线程终止运行(使用E x i t T h r e a d、Te r m i n a t e T h r e a d、E x i t P r o c e s s或Te r m i n a t e P r o c e s s函数),那么互斥对象和正在等待互斥对象的其他线程将会发生什么情况呢?答案是,系统将把该互斥对象视为已经被放弃——拥有互斥对象的线程决不会释放 它,因为该线程已经终止运行。

由于系统保持对所有互斥 对象和线程内核对象的跟踪,因此它能准确的知道互斥对象何时被放弃。当一个互斥对象被放弃时,系统将自动把互斥对象的I D复置为0,并将它的递归计数器复置为0。然后,系统要查看目前是否有任何线程正在等待该互斥对象。如果有,系统将“公平地”选定一个等待线程,将I D设置为选定的线程的I D,并将递归计数器设置为1,同时,选定的线程变为可调度线程。

这与前面的情况相同,差 别在于等待函数并不将通常的WA I T _ O B J E C T _ 0值返回给线程。相反,等待函数返回的是特殊的WA I T _ A B A N D O N E D值。这个特殊的返回值(它只适用于互斥对象)用于指明线程正在等待的互斥对象是由另一个线程拥有的,而这另一个线程已经在它完成对共享资源的使用前终止 运行。这不是可以进入的最佳情况。新调度的线程不知道目前资源处于何种状态,也许该资源已经完全被破坏了。在这种情况下必须自己决定应用程序应该怎么办。

在实际运行环境中,大多数应用程序从不明确检查WA I T _ A B A N D O N E D返回值,因为线程很少是刚刚终止运行(上面介绍的情况提供了另一个例子,说明为什么决不应该调用Te r m i n a t e T h r e a d函数)。

9.6.2 互斥对象与关键代码段的比较

就等待线程的调度而言,互斥对象与关键代码段之间有着相同的特性。但是它们在其他属性方面却各不相同。表9 - 1对它们进行了各方面的比较。

表9-1 互斥对象与关键代码段的比较

特性互斥对象关键代码段
运行速度
是否能够跨进程边界来使用
声明HANDLE hmtx;CRITICAL_SECTION cs;
初始化h m t x = C r e a t e M u t e x(N U L L,FA L S E,N U L L);I n i t i a l i z e C r i t i c a l S e c t i o n ( & e s );
清除C l o s e H a n d l e(h m t x);D e l e t e C r i t i c a l S e c t i o n(& c s);
无限等待Wa i t F o r S i n g l e O b j e c t(h m t x , I N F I N I T E);E n t e r C r i t i c a l S e c t i o n(& c s);
0等待Wa i t F o r S i n g l e O b j e c t Tr y(h m t x , 0);E n t e r C r i t i c a l S e c t i o n(& c s);
任意等待Wa i t F o r S i n g l e O b j e c t(h m t x , d w M i l l i s e c o n d s);不能
释放R e l e a s e M u t e x(h m t x);L e a v e C r i t i c a l S e c t i o n(& c s);
是否能够等待其他内核对象是(使用Wa i t F o r M u l t i p l e O b j e c t s或类似的函数)
 
 
 
互斥和事件对象的区别:事件对象在一个线程中被两次以上等待,第二次会被阻塞,而mutex一旦本某个线程得到,那个线程可以无限次等待,不会阻塞。因此事件往往用于表示某个条件已经满足的信号(也就是事件出现),而mutex则用于让一个线程独占使用某个资源(互斥,就是一个拥有了另外一个不得拥有)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值