我无法直接添加到David、templatetypedef等人给出的优秀答案中 - 如果您想避免线程间通信延迟和资源浪费,请勿使用sleep()循环进行线程间通信。
抢占式调度:
在CPU级别上,中断是关键。操作系统在发生导致其代码被输入的中断之前什么也不做。请注意,在操作系统术语中,中断有两种类型 -“真实”的硬件中断会导致驱动程序运行,“软件中断”-这些是来自已运行线程的操作系统系统调用,可能会导致正在运行的线程集合发生变化。按键、鼠标移动、网络卡、磁盘、页面错误都会生成硬件中断。wait和signal函数以及sleep()属于第二类。当硬件中断导致驱动程序运行时,驱动程序执行其设计的任何硬件管理。如果驱动程序需要向操作系统发出信号,表明某个线程需要运行(例如,磁盘缓冲区现在已满并需要处理),操作系统提供了一个条目机制,驱动程序可以调用该条目机制,而不是直接执行中断返回本身(重要!)。
像上面的例子一样的中断可以使等待的线程准备好运行和/或使正在运行的线程进入等待状态。在处理中断代码之后,操作系统应用其调度算法来决定在中断之前运行的线程集是否与现在应该运行的线程集相同。如果是,则操作系统只需中断返回;如果不是,则操作系统必须抢占一个或多个正在运行的线程。如果操作系统需要抢占正在处理中断的CPU核心之外的运行线程,则必须获得该CPU核心的控制权。它通过“真正的”硬件中断来实现 - 操作系统互处理器驱动程序设置一个硬件信号,硬中断运行要被抢占的线程的核心。
当一个要被抢占的线程进入操作系统代码时,操作系统可以为该线程保存完整的上下文。一些寄存器已经通过中断入口保存在线程的堆栈上,因此保存线程的堆栈指针将有效地“保存”所有这些寄存器,但操作系统通常需要做更多的工作,例如可能需要刷新缓存,需要保存FPU状态,并且如果要运行的新线程属于与要抢占的线程不同的进程,则需要交换内存管理保护寄存器。通常,操作系统会尽快从中断线程堆栈切换到私有操作系统堆栈,以避免对每个线程堆栈施加操作系统堆栈要求。
一旦上下文/ s被保存,操作系统就可以“交换”扩展上下文/ s以使新线程/ s运行。现在,操作系统最终可以加载新线程/ s的堆栈指针并执行中断返回以使其新的准备线程运行。
然后,操作系统什么也不做。运行的线程一直运行,直到发生另一个中断(硬件或软件)。
重要点:
1)操作系统内核应被视为一个大的中断处理程序,它可以决定中断返回到与中断不同的线程集。
2)操作系统可以控制并停止任何进程中的任何线程,无论它处于什么状态或运行在哪个核上。
3)抢占式调度和分派确实会产生一些同步等问题,这些问题在这些论坛上经常讨论。好处是线程对硬件中断有快速响应。如果没有这个功能,你在电脑上运行的所有高性能应用程序-视频流媒体、快速网络等都几乎不可能。
4)操作系统计时器只是一组可以更改运行线程集合的中断之一。 '时间片'(呃——我讨厌那个术语)只有在计算机超载时才会在就绪线程之间进行切换,即就绪线程的数量大于可用于运行它们的 CPU 核数。如果任何试图解释操作系统调度的文本在“中断”之前提到了“时间片”,那么它很可能会造成更多的混乱而不是解释。计时器中断之所以“特殊”,是因为许多系统调用具有超时设置来支持它们的主要功能(好吧,对于 sleep() 函数来说,超时就是其主要功能 :))。