什么是多线程?
多线程是一种允许并发(同时)执行程序的两个或多个部分以最大限度地利用 CPU 的技术。作为一个非常基本的例子,多线程允许您在一个程序中编写代码并在另一个程序中听音乐。程序由进程和线程组成。你可以这样想:
- 程序是像 chrome 这样的可执行文件.exe
- 进程是程序的执行实例。当您双击计算机上的谷歌浏览器图标时,您将启动一个将运行谷歌浏览器程序的进程。
- 线程是进程的最小可执行单元。一个进程可以有多个线程和一个主线程。在此示例中,单个线程可能显示您所在的当前选项卡,而另一个线程可能是另一个选项卡。
多线程示例
考虑运行 IDE 的单个处理器。假设您编辑了一个代码文件,然后单击“保存”。单击“保存”时,它将启动一个工作流,该工作流将导致将字节写出到基础物理磁盘。但是,IO 是一项成本高昂的操作,当字节写出到磁盘时,CPU 将处于空闲状态。
当 IO 发生时,空闲的 CPU 可以处理一些有用的事情,这就是线程进来的地方 - IO 线程被切换出来,UI 线程被调度在 CPU 上,这样,如果你点击屏幕上的其他位置,你的 IDE 仍然响应,不会显得挂起或冻结。
线程可能会给人一种多任务处理的错觉,即使在任何给定的时间点,CPU 只执行一个线程。每个线程在 CPU 上获得一段时间,然后被切换掉。
它启动一个任务,该任务需要等待而不利用 CPU,或者它会在 CPU 上完成其时隙。关于线程调度的工作原理还有更多细微差别和复杂性,但这构成了它的基础。
随着硬件技术的进步,现在拥有多核机器是很常见的。应用程序可以利用这些优势,并让专用 CPU 运行每个线程。
为什么使用多线程?
随着多核的引入,多线程在应用程序的效率方面变得极其重要。对于多个线程和单个内核,您的应用程序必须来回转换,以产生多任务处理的错觉。
使用多个内核,应用程序可以利用底层硬件通过专用内核运行各个线程,从而使应用程序响应速度更快、效率更高。多线程基本上允许您充分利用 CPU 和多核,因此闲置内核没有未开发的处理能力。
开发人员应该使用多线程有几个原因:
- 更高的吞吐量
- 响应式应用程序,给人一种多任务处理的错觉。
- 资源的有效利用。与生成一个全新的进程相比,线程创建是轻量级的,对于在处理 Web 请求时使用线程而不是创建新进程的 Web 服务器,消耗的资源要少得多。
请注意,您不能不断添加线程并期望应用程序运行得更快。更多的线程意味着更多的问题,您必须仔细和深思熟虑地设计它们如何协同工作。在某些情况下,您甚至可能希望完全避免多线程处理,尤其是当您的应用程序执行大量顺序操作时。
对线程工作原理的理解和对并发编程原则的了解将展示开发人员的成熟度和技术深度。这也是在公司找到更高级工作的一个重要区别。
多线程的基本概念
程序、进程和线程
今天的操作系统可以同时运行多个程序。例如,您正在浏览器(程序)中阅读本文,但您也可以在媒体播放器(另一个程序)上收听音乐。
进程是实际执行程序的内容。每个进程都能够运行称为线程的并发子任务。
线程是进程的子任务,如果正确同步,可能会给人一种错觉,即您的应用程序正在同时执行所有操作。如果没有线程,您必须为每个任务编写一个程序,将它们作为进程运行并通过操作系统同步它们。
并发
并发是程序一次处理(不执行)许多事情的能力,通过多线程实现。不要将并发性与并行性混淆,并行性是一次做很多事情。
上下文切换
上下文切换是一种在所有正在运行的进程之间共享 CPU 时间的技术,并且是多任务处理的关键。
线程池
线程池允许您分离任务提交和执行。您可以选择在部署应用程序时公开执行程序的配置,也可以将一个执行程序无缝切换为另一个执行程序。
线程池由分配给执行任务的同构工作线程组成。工作线程完成任务后,将返回到池中。通常,线程池绑定到一个队列,从该队列中取消任务的队列以供工作线程执行。
线程池可以根据其所持有的线程的大小进行调整。线程池也可能替换线程,如果它因意外异常而死亡。使用线程池可以立即减轻手动创建线程的麻烦。关于线程池的重要说明:
线程接收和处理请求时没有延迟,因为在创建线程时不会浪费时间。
系统不会耗尽内存,因为线程不是无限制创建的
微调线程池将使我们能够控制系统的吞吐量。我们可以有足够的线程来保持所有处理器繁忙,但不会太多以至于使系统不堪重负。
如果系统处于负载状态,应用程序将正常降级。