Answers:
简单的答案是,并非所有线程都同时执行。有关更完整的说明,请继续阅读。
通常考虑使用操作系统的任务计划程序来计划应用程序,这样做可以使您在计算机处理另一任务时执行一项任务。在过去,多任务的试金石是在做其他事情的同时格式化软盘。如果您真的想对操作系统进行测试,则可以通过连接到串行端口的调制解调器下载文件的同时格式化软盘。随着硬件功能的强大到足以以有意义的方式进行操作,视频回放有时也会在此类测试中发挥作用。如果OS的任务计划程序可以处理顺利运行的那些任务,那么它可以处理任何事情。
但是,任务调度程序实际上并不调度应用程序(进程),而是调度线程。每个应用程序都有至少一个线程,但可能会使用大量线程将其所做的工作拆分为相关或独立的部分。例如,对于一个应用程序,通常有一个处理用户界面的线程,并在用户启动可能长时间运行的操作时创建另一个线程(这可能是打印,重新计算电子表格,开发环境在做这些事情)。符号查找等)。一些编程环境对程序员不可见地引入了一些线程。例如,Java和.NET可能会进行垃圾回收在一个单独的线程中,这不受程序员的直接控制。一些程序会在早期创建多个线程并将它们合并,因为创建新线程是一项相对昂贵的操作(因此,您不必每次都需要创建一个线程)。进行预览的任何操作通常都是在单独的线程中完成的,因此在生成预览时,UI的其余部分仍会保持响应。等等。综上所述,所有这些意味着在任何时候系统上的线程数量很容易是进程数量的许多倍。
每个线程可以处于几种可能的状态之一,但是最重要的区别是在运行状态,可运行状态和等待状态之间。术语可能略有不同,但这是一般的想法。在任何时候,每个虚拟(由于超线程和类似技术)CPU内核只能运行一个线程(即,执行机器代码指令),但是任何数量的线程都可以运行(这意味着可以选择使用该线程)下次调度程序需要决定允许哪个线程运行时,CPU。等候 (也称为阻塞的)线程只是在等待某事物-最常见的情况可能是它在等待用户,磁盘或网络I / O(尤其是用户输入特别慢)。
您在任务管理器中看到的线程数是处于任何这些状态的线程总数。例如,我在Windows 7系统上键入的内容当前已启动了大约70个进程,但几乎有900个线程。具有处理各种任务的所有后台进程,以及每个进程如何细分为多个线程,这并不是一个离谱的数目。
在抢先式多任务操作系统的任务调度程序的核心部分,要深入技术实施的深度,通常是某种硬件中断挂钩。这意味着内核可以停止CPU的时候它没有执行有用的工作(这是几乎可以肯定的原因之一,如果不是的理由,为什么Linux的检查的HLT
指令,在启动的IA-32兼容的CPU,并且可能会对其他体系结构进行类似的检查),但要知道,在将来合理确定的某个时间,将触发中断并调用任务调度程序。由于中断将被触发而与CPU正在执行的其他工作无关(这就是中断背后的想法),因此调度程序会定期执行,并有机会确定在接下来的时间段内应该执行哪个线程。由于上下文切换相对昂贵,通常可以(至少通过源代码)调整调度程序在线程之间切换的积极程度。切换线程更经常导致系统变得更敏感,但是切换开销意味着完成给定任务集的总时间更长。该最快系统将是仅在无法再运行正在运行的线程(意味着等待某个事物或它已完成其工作)时在线程之间切换的系统,因为这可以最大程度地减少开销,而响应速度最快的系统将在线程之间切换每次调用调度程序时,因为这样可以最大程度地减少在特定线程获得CPU时间之前等待的平均时间。理想的设置通常介于两者之间,这些选择之间的权衡可能是Linux提供多个调度程序供您选择以及通过内核配置进行一些调整的一个重要原因。
另一方面,协作式多任务OS和环境(Windows 3.x是一个示例)依赖于每个应用程序将控制权定期放弃给调度程序。通常有一个专门用于执行此操作的API函数,并且经常有许多API函数会在其内部执行流程中执行此操作,因为它有助于使用户体验更加流畅。只要所有应用程序的行为良好且在任何长时间运行的操作中以较短的间隔让步控制(长时间运行意味着超过一秒钟的时间),该设计方法就行得通,但是应用程序无法阻塞整个系统。这是Windows 3.x在我上面提到的多任务测试中表现不佳而OS / 2的一个重要原因在相同的硬件上执行相同的任务时愉快地漫步:一个应用程序可以告诉软盘驱动器写入特定的扇区,并且在调用返回之前花费的时间实际上是可以测量的(数十到数百毫秒或更多); 一个抢先式多任务处理系统将在其下一个调度的调用中中断其调度程序,请注意,当前“正在运行”的线程实际上已被写入调用阻塞,而只是切换到另一个可运行的线程。(在实践中涉及更多一点,但这是总体思路。)
在抢占式多任务处理和协作环境中,不同线程具有不同优先级的可能性也很大。例如,及时执行通过通信链路接收数据的线程比更新系统时间显示的线程更重要,因此接收线程具有较高的优先级,而时间显示更新程序线程具有较低的优先级。线程优先级在调度程序决定允许执行哪个线程中起着作用(例如,非常简化,高优先级线程应始终在低优先级线程之前执行,因此,即使低优先级线程有工作要做,如果高优先级线程变得可运行,它也将具有优先权),但是此类特定的调度决策不会影响底层机制的设计。
考虑一下拥有1037辆车的四车道高速公路。
您的操作系统需要大量正在运行的进程才能运行许多服务。即使是最简单的图形程序,也将需要多线程编程。当您想到打开了许多程序时,您会发现需要共享计算能力资源。
您的任务管理器显示的是当前系统负载。您的comp规格显示的是并行执行接受了多少个线程(在前端)。无需过多地考虑超线程和多核功能之间的区别,而采用更多的逻辑前端线程,则系统通常会更好。
DoEvents
,它将处理消息队列-但这是在同一线程上完成的,并且会阻止该长时间运行的操作,直到所有消息都被处理为止。(当然,您可以调用Win32 API函数和/或创建其他进程,但那时候您也可以使用其中一种较低级的语言。)
我们应该退后一步,问自己:具有单个CPU的计算机如何有两个线程?
线程是软件实体,而不是硬件。要拥有另一个线程,只需为组成该线程的对象(例如描述符结构和堆栈)提供内存。
操作系统会在不同时间在线程之间切换,例如在某些中断(例如计时器中断)内部或线程向操作系统进行调用时。
在系统中存在的所有线程中,通常只有一个子集处于通常称为“可运行”的状态。可运行线程渴望运行:它们正在执行或坐在“运行队列”中,等待调度程序调度。不可运行的线程被“阻塞”,等待获取一些资源或接收输入,或者“睡眠”,就像被输入阻塞一样,其中“输入”是时间的流逝。当操作系统中的调度程序功能查看处理器的运行队列并选择要执行的其他线程时,就会发生“上下文切换”。
不要被“ hyperthreading”(超级线程)所混淆,“ hyperthreading”是Intel特定硬件功能的名称。