多线程—(互斥功能)关键段,互斥量

本文深入探讨了Windows环境下多线程编程中的关键段(Critical Section)与互斥量(Mutex)的概念及其使用方法。关键段提供了轻量级的线程互斥机制,适用于同一进程内的线程同步;而互斥量作为内核对象,不仅支持进程间线程的互斥访问,还具备处理线程意外终止导致的资源遗弃问题的能力。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过一个经典讲解多线程的博客

http://blog.youkuaiyun.com/morewindows/article/details/7442639

将这四个重要的多线程控制量再次进行相应的整合

关键段

关键段CRITICAL_SECTION的定义吧,WinBase.h中被定义成RTL_CRITICAL_SECTION。而RTL_CRITICAL_SECTIONWinNT.h中声明,它其实是个结构体

typedef struct _RTL_CRITICAL_SECTION {

    PRTL_CRITICAL_SECTION_DEBUGDebugInfo;调试用的

    LONGLockCount;  初始化为-1n表示有n个线程在等待。

    LONGRecursionCount表示该关键段的拥有线程对此资源获得关键段次数,初为0

    HANDLEOwningThread;即拥有该关键段的线程句柄

    HANDLELockSemaphore;实际上是一个自复位事件。

    DWORDSpinCount;旋转锁的设置,单CPU下忽略

RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;


由这个结构可以知道关键段会记录拥有该关键段的线程句柄即关键段是有“线程所有权”概念的。事实上它会用第四个参数OwningThread来记录获准进入关键区域的线程句柄,如果这个线程再次进入,EnterCriticalSection()会更新第三个参数RecursionCount以记录该线程进入的次数并立即返回让该线程进入。其它线程调用EnterCriticalSection()则会被切换到等待状态,一旦拥有线程所有权的线程调用LeaveCriticalSection()使其进入的次数为0时,系统会自动更新关键段并将等待中的线程换回可调度状态。

因此可以将关键段比作旅馆的房卡,调用EnterCriticalSection()即申请房卡,得到房卡后自己当然是可以多次进出房间的,在你调用LeaveCriticalSection()交出房卡之前,别人自然是无法进入该房间。

回到这个经典线程同步问题上,主线程正是由于拥有“线程所有权”即房卡,所以它可以重复进入关键代码区域从而导致子线程在接收参数之前主线程就已经修改了这个参数。所以关键段可以用于线程间的互斥,但不可以用于同步。

 

另外,由于将线程切换到等待状态的开销较大,因此为了提高关键段的性能,Microsoft将旋转锁合并到关键段中,这样EnterCriticalSection()会先用一个旋转锁不断循环,尝试一段时间才会将线程切换到等待状态。

Windows核心编程》第五版的第八章推荐在使用关键段的时候同时使用旋转锁,这样有助于提高性能。值得注意的是如果主机只有一个处理器,那么设置旋转锁是无效的。无法进入关键区域的线程总会被系统将其切换到等待状态。

void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

函数说明:定义关键段变量后必须先初始化。

void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

函数说明:用完之后记得销毁。

void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);

函数说明:系统保证各线程互斥的进入关键区域。

函数功能:离开关关键区域

函数原型:

void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);


互斥量Mutex
互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问。互斥量与关键段的行为非常相似,并且互斥量可以 用于不同进程中的线程互斥访问资源 。使用互斥量 Mutex 主要将用到四个函数。

CreateMutex

函数功能:创建互斥量(注意与事件Event的创建函数对比)

HANDLECreateMutex(

  LPSECURITY_ATTRIBUTESlpMutexAttributes参数表示安全控制,一般直接传入NULL

  BOOLbInitialOwner,   

  参数用来确定互斥量的初始拥有者。如果传入TRUE表示互斥量对象内部会记录创建它的线程的线程ID号并将递归计数设置为1,由于该线程ID非零,所以互斥量处于未触发状态。如果传入FALSE,那么互斥量对象内部的线程ID号将设置为NULL递归计数设置为0,这意味互斥量不为任何线程占用,处于触发状态。(注意为0的时候表示触发状态)

  LPCTSTRlpName       设置互斥量的名称,在多个进程中的线程就是通过名称来确保它们访问的是同一个互斥量。

);

第二个打开互斥量

函数原型:

HANDLEOpenMutex(DWORDdwDesiredAccess,BOOLbInheritHandle,LPCTSTRlpName  //名称 );

第三个触发互斥量

函数原型:

BOOLReleaseMutex (HANDLEhMutex)

函数说明:

访问互斥资源前应该要调用等待函数,结束访问时就要调用ReleaseMutex()来表示自己已经结束访问,其它线程可以开始访问了。

最后一个清理互斥量


由于互斥量是内核对象,因此使用CloseHandle()就可以(这一点所有内核对象都一样)。

斥量也是有“线程拥有权”概念的。“线程拥有权”在关键段中有详细的说明,这里就不再赘述了。另外由于互斥量常用于多进程之间的线程互斥,所以它比关键段还多一个很有用的特性——“遗弃”情况的处理。比如有一个占用互斥量的线程在调用 ReleaseMutex() 触发互斥量前就意外终止了(相当于该互斥量被“遗弃”了),那么所有等待这个互斥量的线程是否会由于该互斥量无法被触发而陷入一个无穷的等待过程中了?这显然不合理。因为占用某个互斥量的线程既然终止了那足以证明它不再使用被该互斥量保护的资源,所以这些资源完全并且应当被其它线程来使用。因此在这种“遗弃”情况下,系统自动把该互斥量内部的线程 ID 设置为 0 ,并将它的递归计数器复置为 0 ,表示这个互斥量被触发了。然后系统将 公平地 选定一个等待线程来完成调度(被选中的线程的 WaitForSingleObject() 会返回 WAIT_ABANDONED_0 )。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值