关键段和互斥量的区别

关键段是用户态下面的同步方法,前者不会使线程进入内核模式,而互斥量是内核态的同步方法,在等待的时候,会挂起并进入内核模式;

我们知道,由用户态进入内核态是需要很多时间周期的,大约一千个 CPU 周期。

所以它们最大的区别就是前者的速度快,后者速度慢。


关键段有一个“遗弃问题”。我们知道,LeaveCriticalSection 会检查结构内部的成员变量并将计数器减 1,此计数器表示调用线程获准访问共享资源的次数。如果计数器大于 0,则 LeaveCriticalSection 不进行其它处理,表示当前线程还可以进入关键段。如果变为 0 了,则更新成员变量,表示没有任何线程正在访问关键段,然后检查有没有线程正在等待访问关键段,即,有些线程超出了自旋次数,进入了等待,则 LeaveCriticalSection 通过事件内核对象唤醒线程。最后再将自旋中测试赋值的那个量的值重置,让所有线程再展开下一轮竞争。

所谓的遗弃问题是指,用户忘记掉用 LeaveCriticalSection 这个函数且终止了,由于关键段数据结构里面有引用计数,表示当前线程有几次访问权,一旦没有正确地 leave ,则这个计数不会为 0 ,后续的线程再也进不了这个关键段了。

而互斥量不存在这样的问题,即使用户忘记调用 ReleaseMutex  且该线程终止了,但由于互斥量有“线程相关记忆”,它会记得是哪一个线程占有当前的互斥量(它是惟一一个有线程相关联记忆的内核对象),下一次在任意一个线程调用 waitForSingleObject 的时候,互斥量对象就会检查互斥量数据结构里面记录的线程是否已经消亡了,如果消亡了,则会将线程拥有者标记为 0,表示没有线程正在占有互斥量,且把引用计数置为 0,开展下一次互斥量的竞争。

也许你看到这里,会说,为什么关键段不也这样实现呢?即,在 EnterCriticalSection 内部去测试关键段数据结构中的线程的有效性? 这是因为,互斥量是内核对象,线程也是内核对象,所以,在互斥量的处理逻辑里面,它很快就能查询一个线程是什么状态,而在关键段里面虽然也可以通过向内核发送请求,查询线程状态,但这是非常不明智的,因为一个系统调用是非常耗时的,而且如果每次在 EnterCriticalection 时都检查线程有效性更加拖垮了效率。记住,关键段本来就是作为用户态同步实现的,只有完全工作于用户模式下才有效率可言,这也是它存在的理由!


关键段不支持任何长时间的等待,它只支持等待 0 或者等待无限时长。互斥量可以使用 WaitForSingleObject 等待任意长时间,也可以使用 WaitForMultiObjects 来一次等待多个(可以是其它类型的)内核对象。


另外,由于互斥量是一个内核对象,它被操作系统拥有,而内核对象可以在不同进程之间拷贝。所以互斥量可以 用于不同进程之间互斥,而关键段则没有这样的效果。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值