zmi*_*pie 17 multithreading mutex locking thread-safety blocking
我一直在阅读多线程和共享资源访问,其中一个(对我而言)新概念是互斥锁.我似乎无法找到的是发现"临界区"被锁定的线程实际发生的情况.它说在许多地方线程被"阻止",但这意味着什么?是否暂停,锁定解除后会恢复吗?或者它会在"运行循环"的下一次迭代中再次尝试?
我问的原因是因为我想要系统提供的事件(鼠标,键盘等),它们(显然)在主线程上传递,在我的辅助线程的运行循环中的一个非常特定的部分处理.因此无论发生什么事件,我都会在自己的数据结构中排队.显然,数据结构需要一个互斥锁,因为它被两个线程修改.缺少的难题是:当事件在主线程上的函数中传递时会发生什么,我想将其排队,但是队列被锁定了?主线程是否会被暂停,还是会跳过锁定的部分并超出范围(丢失事件)?
Jer*_*man 14
被阻止意味着执行陷入困境; 通常,线程由系统进入休眠状态并将处理器转移到另一个线程.当线程被阻塞以尝试获取互斥锁时,执行会在互斥锁释放时恢复,但如果另一个线程在可以之前抓取互斥锁,线程可能会再次阻塞.
通常会有一个try-lock操作,如果可能的话会抓取互斥锁,否则会返回错误.但是你最终必须将当前事件移动到该队列中.此外,如果您将事件延迟移动到处理它们的线程,则无论如何应用程序都将无响应.
实际上,队列就是一种不使用互斥锁就可以逃脱的情况.例如,Mac OS X的(也可能是IOS)是将OSAtomicEnqueue()和OSAtomicDequeue()功能(参见man atomic或<libkern/OSAtomic.h>),其利用特定处理器的原子操作,以避免使用锁.
但是,为什么不直接处理主线程上的事件作为主运行循环的一部分?
想到它的最简单方法是将被阻塞的线程置于等待("休眠")状态,直到由持有它的线程释放互斥锁.此时,操作系统将"唤醒"等待互斥锁的其中一个线程,让它获取并继续.就好像操作系统只是将阻塞的线程放在架子上,直到它具有需要继续的东西.直到操作系统从架子上取下线程,它才做任何事情.确切的实现 - 接下来哪个线程,无论是全部被唤醒还是排队 - 将取决于您的操作系统以及您正在使用的语言/框架.
来不及回答,但我可能会促进理解。我更多地从实施的角度而不是理论文本进行讨论。
“阻塞”这个词是一种技术上的同音异义词。人们可以用它来睡觉或等待。必须在使用上下文中理解该术语。
阻塞意味着等待- 假设在 SMP 系统上,线程 B 想要获取其他线程 A 持有的自旋锁。其中一种机制是禁用抢占并继续在处理器上旋转,除非 B 获得它。另一种机制可能是一种有效的机制,是允许其他线程使用处理器,以防 B 无法通过简单的尝试获得它。因此,我们调度线程 B(因为启用了抢占)并将处理器交给其他线程 C。在这种情况下,线程 B 只是在调度程序的队列中等待,然后轮到它回来。理解 B 不是在休眠,只是被动地等待,而不是忙等待和消耗处理器周期。在 BSD 和 Solaris 系统上,有像旋转门这样的数据结构来实现这种情况。
阻塞意味着休眠- 如果线程 B 进行了系统调用,例如read()等待来自网络套接字的数据,则在获取数据之前它无法继续。因此,一些文本随意使用术语阻塞作为“...为 I/O 阻塞”或“...在阻塞系统调用中”。实际上,线程 B 正在休眠。有一些特定的数据结构称为睡眠队列- 很像机场上的豪华候车室:-)。当操作系统检测到数据可用时,线程将被唤醒,就像等候室的服务员一样。