给定一个单核 CPU,使用线程编码有什么好处?
至少对于 Java 实现,考虑到单核限制,自然扩展到任何其他语言似乎很直观,您可能有多个线程执行各种操作,但进程是有时间限制和切换的。
给定进程 A 和进程 B:
执行流程 A 的一半,完成流程 B,然后完成流程 A 的后半部分 VS 执行流程 A 然后 B 有什么好处?
似乎线程之间的切换会引入时间延迟,这会延长两个进程的总体完成时间,而不是切换并且只完成 A 然后 B。
给定一个单核 CPU,使用线程编码有什么好处?
至少对于 Java 实现,考虑到单核限制,自然扩展到任何其他语言似乎很直观,您可能有多个线程执行各种操作,但进程是有时间限制和切换的。
给定进程 A 和进程 B:
执行流程 A 的一半,完成流程 B,然后完成流程 A 的后半部分 VS 执行流程 A 然后 B 有什么好处?
似乎线程之间的切换会引入时间延迟,这会延长两个进程的总体完成时间,而不是切换并且只完成 A 然后 B。
在单核系统上使用线程的原因只是为了让原本会使用所有 CPU 的进程被其他需要尽快完成的任务抢占。使系统具有多线程的最常见原因是即使在执行长时间计算时也具有响应式用户界面。
当然,任何操作都可能需要很长时间(读取文件、访问数据库、调整照片大小、重新计算电子表格),这些操作可以在单独的线程上执行,让响应用户输入的线程来操作整个时间。
例如,20 年前,很少有多 CPU 系统或允许多线程的操作系统,因此几乎每个程序都是单线程的,并且创建了许多框架以允许系统具有 UI,而且我仍然这样做/O。对此的标准机制是事件循环,其中所有事件(UI、网络、计时器等)都在一个大循环中处理。
这种类型的系统意味着 UI 在文件 I/O 和计算等操作期间会被暂停。为了不过多占用 UI,您必须以块的形式执行 I/O(例如,一次读取 4k 文件),处理块之间的任何传入 UI 事件。这实际上只是保持系统运行的 hack,但很难让系统像这样平稳运行,因为您不知道需要多久处理一次事件。
解决方案是有一个单独的线程来重新计算您的电子表格或编写您的文件。这样,操作系统可以为这些线程提供公平的时间片,同时仍抢占它们运行 UI,从而使 UI 始终响应。
执行线程不一定做任何有用的事情。典型的例子是从磁盘读取——数据不会再存在几毫秒,在此期间处理器将处于未使用状态。线程允许程序的一部分使用 CPU,而程序的其他部分正在等待操作完成。
有很多原因。Wikipedia 在其关于线程的页面上给出了不错的概述。
这里有一些OTOH:
如果您的程序必须“同时”做几件事,那么线程是一个很好的选择,特别是其中一些任务运行时间很长。否则,您会发现自己在程序中编写看起来像操作系统调度程序的代码,如果您下面的操作系统已经拥有完美的操作系统,这总是浪费时间。您会发现您的源代码主要是“调度程序”,而不是太多的“程序”,这非常不雅。一个好的线程程序可以在源代码中非常优雅和经济,这样可以让自己看起来很好,也可以节省时间。
一些运行时间得到/弄错了。在 Ada 的早期,运行时环境会自己进行线程调度,而且从来都不是很令人满意。这部分是因为虽然 Ada 语言规范包含线程的概念,但我们当时拥有的操作系统通常不提供它们。当编译器编写者开始使用底层操作系统线程时,Ada 变得更好了。
同样,Python 并没有真正正确地使用底层操作系统线程;它用全局解释器锁破坏了它。Python 通过使用多处理来回避整个问题(在 Windows 主机上不一定是好事......)。
早期版本的 Windows 也不做线程,他们做协作多任务。这取决于整个机器中的每个进程至少不时调用任何操作系统例程。每个操作系统例程都会首先咨询“调度程序”,看看是否还有其他东西在等待运行,然后才能继续代表程序执行它应该做的任何事情。那时有许多糟糕的程序不会玩球并占用整个机器。当其他东西开始计算长度时,你无法继续玩纸牌游戏。
你的程序的心智模型是什么?
如果它取决于可能以不可预测的顺序发生的多个外部输入,并且如果您想要响应这些输入的操作并不简单并且可以在时间上重叠......
那么为每个输入请求分配一个单独的线程并让该线程执行该请求所需的响应是有意义的。
因此,例如,如果您的程序正在等待来自外部通道的输入请求,并且每个请求都必须触发其自己的传出和传入消息协议,那么它可以非常简化创建新线程的代码(或重用旧的)每个请求。
不知何故,人们似乎进入了劳动力市场,认为线程只是为了速度(通过并行性)。这是一种用途,只要它允许多个 CPU 芯片启动,但它绝不是唯一的用途。