我无法直接添加到 David、templatetypedef 等给出的出色答案中 - 如果您想避免线程间通信延迟和资源浪费,请不要使用 sleep() 循环进行线程间通信。
抢先调度/调度:
在 CPU 级别,中断是关键。在发生导致其代码被输入的中断之前,操作系统什么都不做。请注意,在操作系统术语中,中断有两种形式 - 导致驱动程序运行的“真实”硬件中断和“软件中断”——这些是来自已运行线程的操作系统系统调用,可能会导致一组正在运行的线程改变。按键、鼠标移动、网卡、磁盘、页面错误都会产生硬件中断。等待和信号函数以及 sleep() 属于第二类。当硬件中断导致驱动程序运行时,驱动程序执行它设计的任何硬件管理。如果驱动程序需要通知操作系统某个线程需要运行,(也许磁盘缓冲区现在已满并需要处理),
像上述示例这样的中断可以使正在等待的线程准备好运行和/或可以使正在运行的线程进入等待状态。在处理完中断的代码之后,操作系统应用它的调度算法来决定在中断之前运行的线程集是否与现在应该运行的线程集相同。如果是,操作系统只是中断返回,如果不是,操作系统必须抢占一个或多个正在运行的线程。如果操作系统需要抢占在不是处理中断的 CPU 内核上运行的线程,它必须获得对该 CPU 内核的控制权。它通过“真正的”硬件中断来做到这一点——操作系统间处理器驱动程序设置一个硬件信号,硬中断运行要被抢占的线程的内核。
当要被抢占的线程进入操作系统代码时,操作系统可以为该线程保存完整的上下文。一些寄存器已经通过中断入口保存到线程的堆栈中,因此保存线程的堆栈指针将有效地“保存”所有这些寄存器,但操作系统通常需要做更多的事情,例如。可能需要刷新缓存,可能需要保存 FPU 状态,并且如果要运行的新线程属于与要抢占的进程不同的进程,则需要换出内存管理保护寄存器. 通常,操作系统会尽快从中断线程堆栈切换到私有操作系统堆栈,以避免将操作系统堆栈要求强加到每个线程堆栈上。
一旦保存了上下文,操作系统就可以为要运行的新线程“交换”扩展上下文。现在,操作系统终于可以为新线程加载堆栈指针并执行中断返回以使其新的就绪线程运行。
然后操作系统什么也不做。正在运行的线程一直运行,直到发生另一个中断(硬中断或软中断)。
要点:
1)操作系统内核应该被视为一个大的中断处理程序,它可以决定中断返回到与被中断线程不同的线程集。
2)操作系统可以控制并在必要时停止任何进程中的任何线程,无论它处于什么状态或它可能运行在什么核心上。
3)抢先调度和调度确实会产生所有这些论坛上发布的同步等问题。最大的好处是线程级对硬中断的快速响应。如果没有这个,您在 PC 上运行的所有高性能应用程序——视频流、快速网络等,几乎都是不可能的。
4) The OS timer is just one of a large set of interrupts that can change the set of running threads. 'Time-slicing', (ugh - I hate that term), between ready threads only occurs when the computer is overloaded, ie. the set of ready threads is larger than the number of CPU cores available to run them. If any text purporting to explain OS scheduling mentions 'time-slicing' before 'interrupts', it is likely to cause more confusion than explanation. The timer interrupt is only 'special' in that many system calls have timeouts to back up their primary function, (OK, for sleep(), the timeout IS the primary function:).