休眠线程如何工作?


14

当您休眠线程时,实际上是什么情况?

我看到休眠线程“在给定的时间段内暂停了当前线程”。但是,它是如何工作的呢?

根据Thread.sleep()在内部如何工作以及Thread.sleep如何真正工作?

  • 睡眠时间将取决于某些系统特定的粒度
  • 睡眠受阻
  • 线程离开CPU并停止执行
  • 线程在睡眠时不占用CPU时间

我只是不太了解这一切意味着什么的内部和基本机制。

我知道有一个称为调度程序的东西负责线程之间的切换。

消息来源似乎表明这随操作系统(或硬件?)而变化,并且大多数线程被赋予1ms-60ms左右的时间,以便在CPU切换到另一个线程之前执行某些操作。

但是,当线程休眠时(例如,数秒),它如何恢复?我猜想其中涉及一个计时器,它是主板的时钟吗?它与CPU时钟速率有关吗?

即使涉及计时器,CPU如何知道何时该再次关注该线程?是否不必经常检查线程以查看其是否准备就绪?这不是有效的轮询,因此浪费CPU时间吗?

是在休眠特定于线程语言的线程,还是由操作系统负责,还是CPU特定的事物?

有人可以通过诸如调度程序之类的基本解释以及CPU在所有这些过程中的作用向我解释一下吗?


2
唤醒睡眠线程再次通过定时中断进行工作,定时中断通常由中断时钟产生,中断时钟是与处理指令的CPU核心部分分开的硬件组件。这样就避免了轮询的需要。也许Quora上的这种解释会澄清一些事情:quora.com/How-does-threading-work-at-CPU-level
Doc Brown

1
大多数JVM实际上并未实现多线程:它们所做的只是使用基础操作系统的多线程功能。如果您真的想知道它是如何工作的,那么有很多关于操作系统设计的书籍。
所罗门慢

Answers:


11

运行一个程序所涉及的不仅仅是该程序中的代码。

在多进程OS中运行的任何程序都在OS的调度程序的控制下,并且调度程序确实维护一个明确的表,该表说明正在运行的进程,在CPU周期可用时正在等待运行的进程,甚至没有运行的进程试图跑步(睡觉)。调度程序通常根据进程的优先级和执行历史为进程分配偶数大小的时间片。最终,该循环由硬件中断驱动,通常由主板上的振荡器产生。

睡眠始终是编程语言只能支持的功能,因为它将在其中执行的运行时环境支持它。一个正常的程序不能暂停本身,它只能告诉调度会如何待处理-和调度是没有义务的手段,甚至总是能满足这个愿望的。想一想笔记本电脑已经关闭并进入休眠状态;主板的振荡器一直在跳动,但是由于调度程序没有运行,因此无论其优先级如何,都无法运行任何进程。


(通常)对于进程是正确的,但对于线程(有时依赖于语言)并非总是如此。线程可以独立于OS来实现,并且可以是协作的或抢占的。例如,Ruby具有“纤维”(除了线程)。红宝石纤维是协同调度的。
david25272 '16

是的,只有本机线程才这样工作。VM通过字节编译语言执行程序来调度绿色线程。通常,当本地线程不可用时,或者在运行的线程安全性不佳时(VM有时可以确保多线程程序的语义保持OS调度程序无法保证的正确性)使用它们。
凯莉安·佛斯

7

就像布朗博士在评论中提到的那样,中断是关键,而不仅仅是睡眠。

中断是一个硬件信号,表明处理器应停止正在执行的操作并运行一段代码。外部设备在需要处理器注意时会触发中断:例如,磁盘已完成读取数据,或者已按下键,或者主板上的倒数计时器达到零。

中断处理代码通常很小而且非常快。例如,当磁盘指示一个块已被复制到内存时,操作系统可能只是将该事实记录在某个位置的“就绪块”列表中,然后返回到它正在执行的其他操作。您不希望CPU将所有时间都花在中断处理代码上,而不运行用户代码。

的中断驱动的代码的1张是不必然是小的调度器。它由倒数计时器发出的信号触发,并在系统运行时检查系统的状态。这通常将包括识别准备运行的进程(例如,因为它们正在等待的块已到达内存)以及已用完时间片的那些进程。

因此,当您执行线程睡眠时,您正在执行的操作是告诉操作系统(1)您正在放弃时间片,并且(2)您应该在经过特定时间后才再次唤醒。

每当调度程序运行时,它都会查看您的线程,并且仅在经过该时间后才将其标记为“准备运行”。这是一种轮询,但不是“忙循环”轮询,因为它是由中断触发的。它也不是那么昂贵:通常一次仅运行约一千个线程。

这也应该使您了解为什么睡眠时间不正确:当线程准备好运行时,可能还有其他线程仍在运行并且没有耗尽其时间片。或者可能有更高优先级的线程可以运行。


这就是“现代”操作系统的工作方式。在较早的操作系统(例如Windows 3或OS X之前的Mac OS)中,进程必须对操作系统产生yield()控制。不幸的是,如果程序陷入循环或以某种方式死锁,则可能永远无法产生控制权,整个系统将挂起。
david25272 '16

@ david25272-我想我会使用“简单”一词,而不是“较旧”,因为从1960年代开始就有一些系统抢占了多任务处理能力。尽管确实确实需要协同执行多任务处理,才能使活动线程做一些事情来释放其对处理器的控制(通常是通过进行阻塞系统调用,而不是显式地让步),但我不相信有很多通用程序员今天使用带有协作线程模型的操作系统的人。
kdgregory
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.