互斥锁:“阻塞”是什么意思?

时间:2010-10-20 21:33:39

标签: multithreading locking thread-safety mutex blocking

我一直在阅读多线程和共享资源访问,其中一个(对我而言)新概念是互斥锁。我似乎无法找到的是发现“临界区”被锁定的线程实际发生的情况。它说在许多地方线程被“阻止”,但这意味着什么?是否暂停,锁解除后会恢复吗?或者它会在“运行循环”的下一次迭代中再次尝试?

我问的原因是因为我想要系统提供的事件(鼠标,键盘等),它们(显然)在主线程上传递,在运行循环中的一个非常特定的部分处理我的第二个帖子。因此无论发生什么事件,我都会在自己的数据结构中排队。显然,数据结构需要一个互斥锁,因为它被两个线程修改。缺少的难题是:当事件在主线程上的函数中传递时会发生什么,我想将其排队,但是队列被锁定了?主线程是否会暂停,还是会跳过锁定的部分并超出范围(丢失事件)?

4 个答案:

答案 0 :(得分:14)

阻止意味着执行陷入困境;通常,线程由系统进入休眠状态并将处理器转移到另一个线程。当线程被阻塞尝试获取互斥锁时,执行会在互斥锁被释放时恢复,但如果另一个线程在可能之前抓取互斥锁,线程可能会再次阻塞。

通常会尝试使用try-lock操作来获取互斥锁,否则会返回错误。但是你最终必须将当前事件移动到该队列中。此外,如果您将事件延迟移动到处理它们的线程,则无论如何应用程序都将无响应。

队列实际上是一种情况,您可以在不使用互斥锁的情况下逃脱。例如,Mac OS X(可能还有iOS)提供了OSAtomicEnqueue()OSAtomicDequeue()函数(请参阅man atomic<libkern/OSAtomic.h>),这些函数利用特定于处理器的原子操作来避免使用一把锁。

但是,为什么不直接处理主线程上的事件作为主运行循环的一部分?

答案 1 :(得分:8)

最简单的方法是将被阻塞的线程置于等待(“休眠”)状态,直到互斥锁由持有它的线程释放。此时,操作系统将“唤醒”等待互斥锁的其中一个线程,让它获取并继续。就好像操作系统只是将阻塞的线程放在架子上,直到它具有需要继续的东西。直到操作系统从架子上取下线程,它才做任何事情。确切的实现 - 接下来哪个线程,无论是全部被唤醒还是排队 - 将取决于您的操作系统以及您正在使用的语言/框架。

答案 2 :(得分:3)

回答太晚,但我可能会促进理解。我从实施角度而不是理论文本中谈论更多。

&#34;阻止&#34;是一种技术谐音。人们可以将它用于睡眠或仅仅等待。该术语必须在使用的背景下理解。

阻止意味着等待 - 假设在SMP系统上线程B想要获取由其他线程A持有的自旋锁。其中一种机制是禁用抢占并继续在处理器上旋转,除非B得到它。另一种机制可能是一种有效的机制,即允许其他线程使用处理器,以防B无法轻松获取它。因此,我们计划出线程B(启用抢占)并将处理器提供给其他线程C.在这种情况下,线程B只是在调度程序的队列中等待,然后轮到它。明白B不是在等待而是被动地等待而不是忙碌等待和烧毁处理器周期。在BSD和Solaris系统上,有像 turnstiles 这样的数据结构来实现这种情况。

阻止意味着正在休眠 - 如果线程B改为从网络套接字等 read()等系统调用,则在获取它之前无法继续。因此,有些文本随意使用术语阻塞作为&#34; ...阻止I / O&#34;或者&#34; ...阻止系统调用&#34;。实际上,线程B正在睡觉。有一些特定的数据结构称为睡眠队列 - 就像空中港口的豪华候诊室:-)。当操作系统检测到数据可用时,该线程将被唤醒,就像候诊室的服务员一样。

答案 3 :(得分:1)

阻止就是这个意思。它被封锁了。直到有能力才会进行。你没有说你正在使用哪种语言,但大多数语言/库都有锁定对象,你可以“尝试”取锁,然后根据你是否成功继续进行不同的操作。

但是,例如,在Java同步块中,您的线程将停止,直到它能够获取监视器(互斥锁,锁定)。 java.util.concurrent.locks.Lock接口描述了锁定对象,这些锁定对象在锁定获取方面具有更大的灵活性。