我一直在阅读有关多线程和共享资源访问的内容,(对我来说)许多新概念之一就是互斥锁。我似乎无法弄清楚发现“关键部分”被锁定的线程实际上发生了什么。它在很多地方都说线程被“阻塞”,但这是什么意思呢?是否已暂停,解除锁后是否会恢复?或者它会在“运行循环”的下一次迭代中再次尝试?
我问的原因是因为我希望系统提供的事件(鼠标、键盘等)(显然)在主线程上传递,并在辅助线程的运行循环中的非常特定的部分中进行处理。因此,无论传递什么事件,我都会在自己的数据结构中排队。显然,数据结构需要互斥锁,因为它正在被两个线程修改。缺少的难题是:当主线程上的函数中传递事件时会发生什么,我想将其排队,但队列被锁定?主线程会被挂起,还是会跳过锁定的部分并超出范围(丢失事件)?
I've been reading up on multithreading and shared resources access and one of the many (for me) new concepts is the mutex lock. What I can't seem to find out is what is actually happening to the thread that finds a "critical section" is locked. It says in many places that the thread gets "blocked", but what does that mean? Is it suspended, and will it resume when the lock is lifted? Or will it try again in the next iteration of the "run loop"?
The reason I ask, is because I want to have system supplied events (mouse, keyboard, etc.), which (apparantly) are delivered on the main thread, to be handled in a very specific part in the run loop of my secondary thread. So whatever event is delivered, I queue in my own datastructure. Obviously, the datastructure needs a mutex lock because it's being modified by both threads. The missing puzzle-piece is: what happens when an event gets delivered in a function on the main thread, I want to queue it, but the queue is locked? Will the main thread be suspended, or will it just jump over the locked section and go out of scope (losing the event)?
发布评论
评论(4)
阻塞意味着执行被卡在那里;通常,该线程被系统置于睡眠状态,并将处理器让给另一个线程。当一个线程在尝试获取互斥体时被阻塞时,当互斥体被释放时,执行会恢复,但如果另一个线程先于它获取互斥体,则该线程可能会再次阻塞。
通常有一个尝试锁定操作,如果可能的话,会获取互斥锁,如果没有,将返回错误。但您最终必须将当前事件移至该队列中。此外,如果您延迟将事件移动到处理它们的线程,则应用程序无论如何都会变得无响应。
队列实际上是一种可以不使用互斥锁的情况。例如,Mac OS X(也可能是 iOS)提供了
OSAtomicEnqueue()
和OSAtomicDequeue()
函数(请参阅manatomic
或>
)利用特定于处理器的原子操作来避免使用锁。但是,为什么不将主线程上的事件作为主运行循环的一部分来处理呢?
Blocked means execution gets stuck there; generally, the thread is put to sleep by the system and yields the processor to another thread. When a thread is blocked trying to acquire a mutex, execution resumes when the mutex is released, though the thread might block again if another thread grabs the mutex before it can.
There is generally a try-lock operation that grab the mutex if possible, and if not, will return an error. But you are eventually going to have to move the current event into that queue. Also, if you delay moving the events to the thread where they are handled, the application will become unresponsive regardless.
A queue is actually one case where you can get away with not using a mutex. For example, Mac OS X (and possibly also iOS) provides the
OSAtomicEnqueue()
andOSAtomicDequeue()
functions (seeman atomic
or<libkern/OSAtomic.h>
) that exploit processor-specific atomic operations to avoid using a lock.But, why not just process the events on the main thread as part of the main run loop?
最简单的思考方式是,被阻塞的线程处于等待(“睡眠”)状态,直到持有该互斥锁的线程释放该互斥锁。此时,操作系统将“唤醒”等待互斥体的线程之一,并让它获取互斥体并继续。就好像操作系统只是将被阻塞的线程放在架子上,直到它拥有继续运行所需的东西。在操作系统将线程下架之前,它不会执行任何操作。确切的实现(哪个线程接下来要执行,它们是否全部被唤醒或排队)将取决于您的操作系统以及您使用的语言/框架。
The simplest way to think of it is that the blocked thread is put in a wait ("sleeping") state until the mutex is released by the thread holding it. At that point the operating system will "wake up" one of the threads waiting on the mutex and let it acquire it and continue. It's as if the OS simply puts the blocked thread on a shelf until it has the thing it needs to continue. Until the OS takes the thread off the shelf, it's not doing anything. The exact implementation -- which thread gets to go next, whether they all get woken up or they're queued -- will depend on your OS and what language/framework you are using.
回答得太晚了,但我可以促进理解。我更多地是从实施的角度而不是理论文本来谈论。
“阻塞”这个词是一种技术上的同音异义词。人们可能会用它来睡觉或仅仅等待。该术语必须在使用上下文中进行理解。
阻塞意味着等待 - 假设在 SMP 系统上,线程 B 想要获取由其他线程 A 持有的自旋锁。其中一种机制是禁用抢占并继续在处理器上旋转,除非 B 获取它。另一种机制可能是一种有效的机制,即允许其他线程使用处理器,以防 B 无法轻松尝试获得它。因此,我们调度线程 B(因为启用了抢占)并将处理器交给其他线程 C。在这种情况下,线程 B 只是在调度程序的队列中等待,然后轮到它返回。了解 B 并不是在睡觉,只是被动地等待,而不是忙等待和燃烧处理器周期。在 BSD 和 Solaris 系统上,有像 turnstiles 这样的数据结构来实现这种情况。
阻塞意味着休眠 - 如果线程 B 进行了诸如 read() 之类的系统调用,等待来自网络套接字的数据,则在获取数据之前它无法继续进行。因此,有些文本随意使用术语“阻塞”作为“...阻塞I/O”或“...阻塞系统调用”。实际上,线程 B 正在休眠。有一些特定的数据结构,称为睡眠队列 - 很像机场的豪华候机室:-)。当操作系统检测到数据可用时,该线程将被唤醒,就像候诊室的服务员一样。
Too late to answer but I may facilitate the understanding. I am talking more from implementation perspective rather than theoretical texts.
The word "blocking" is kind of technical homonym. People may use it for sleeping or mere waiting. The term has to be understood in context of usage.
Blocking means Waiting - Assume on an SMP system a thread B wants to acquire a spinlock held by some other thread A. One of the mechanisms is to disable preemption and keep spinning on the processor unless B gets it. Another mechanism probably, an efficient one, is to allow other threads to use processor, in case B does not gets it in easy attempts. Therefore we schedule out thread B (as preemption is enabled) and give processor to some other thread C. In this case thread B just waits in the scheduler's queue and comes back with its turn. Understand that B is not sleeping just waiting rather passively instead of busy-wait and burning processor cycles. On BSD and Solaris systems there are data-structures like turnstiles to implement this situation.
Blocking means Sleeping - If the thread B had instead made system call like read() waiting data from network socket, it cannot proceed until it gets it. Therefore, some texts casually use term blocking as "... blocked for I/O" or "... in blocking system call". Actually, thread B is rather sleeping. There are specific data-structures known as sleep queues - much like luxury waiting rooms on air-ports :-). The thread will be woken up when OS detects availability of data, much like an attendant of the waiting room.
封锁就是这个意思。它被阻止了。在有能力之前不会继续进行。您没有说明您正在使用哪种语言,但大多数语言/库都有锁定对象,您可以在其中“尝试”获取锁定,然后根据您是否成功继续执行不同的操作。
但是,例如,在 Java 同步块中,您的线程将停止,直到它能够获取监视器(互斥锁、锁)。 java.util.concurrent.locks.Lock 接口描述了在锁获取方面具有更大灵活性的锁对象。
Blocking means just that. It is blocked. It will not proceed until able. You don't say which language you're using, but most languages/libraries have lock objects where you can "attempt" to take the lock and then carry on and do something different depending on whether you succeeded or not.
But in, for example, Java synchronized blocks, your thread will stall until it is able to acquire the monitor (mutex, lock). The
java.util.concurrent.locks.Lock
interface describes lock objects which have more flexibility in terms of lock acquisition.