线程与进程:看看它们在程序中是如何工作的

2020-07-26 17:47:22

您是否经常听到与计算机程序相关的术语“线程”,但您并不确定它的确切含义?流程呢?您可能知道线程在某种程度上与程序和进程密切相关,但是如果您不是计算机科学专业的学生,那么您的理解可能就是这样。

如果你是一名程序员,了解这些术语的含义是绝对必要的,但对普通计算机用户来说,理解它们也是很有用的。能够查看和了解Macintosh上的活动监视器、Windows上的Task Manager或Linux上的Top可以帮助您排除计算机上哪些程序导致问题的故障,或者您是否需要安装更多内存才能使系统运行得更好。

让我们花几分钟时间深入研究计算机程序的世界,并理清这些术语的含义。我们将简化和概括一些概念,但是我们涵盖的一般概念应该有助于澄清术语之间的区别。

首先,您可能意识到程序是存储在您的计算机上用于完成特定任务的代码。有许多类型的程序,包括帮助您的计算机运行并且是操作系统的一部分的程序,以及完成特定任务的其他程序。这些特定于任务的程序也称为“应用程序”,可以包括文字处理、Web浏览或通过电子邮件向另一台计算机发送消息等程序。

程序通常以计算机可以执行的形式存储在磁盘或非易失性存储器中。在此之前,它们是使用C、Lisp、Pascal等编程语言创建的,或者使用涉及逻辑、数据和设备操作、递归和用户交互的指令创建的。最终结果是编译成二进制形式(1和0)以便在计算机上运行的代码的文本文件。另一种类型的程序被称为“解释的”,并且不是为了运行而预先编译,而是在运行时解释成可执行代码。一些常见的、通常是解释的编程语言有Python、PHP、JavaScript和Ruby。

然而,最终结果是相同的,因为当程序运行时,它以二进制形式加载到内存中。计算机的CPU(中央处理器)只能识别二进制指令,因此这是程序运行时需要采用的形式。

二进制是计算机的母语,因为基本级别的电路有两种状态,开或关,用1或0表示。在我们每天使用的常用编号系统,基数10,每个数字位置可以是从0到9的任何数字。在基数2(或二进制)中,每个位置要么是0,要么是1。(在未来的一篇博客文章中,我们可能会讨论量子计算,它超越了计算中只有1和0的概念。)。

该程序已以二进制形式加载到计算机内存中。这次又是什么?

一个正在执行的程序需要的不仅仅是告诉计算机该做什么的二进制代码。该程序需要内存和各种操作系统资源才能运行。“进程”是我们所说的已经加载到内存中的程序,以及它运行所需的所有资源。“操作系统”是分配所有这些资源的大脑,它有不同的风格,如MacOS、iOS、Microsoft Windows、Linux和Android。操作系统负责管理将您的程序转换为运行进程所需的资源。

每个进程需要的一些基本资源是寄存器、程序计数器和堆栈。“寄存器”是作为计算机处理器(CPU)一部分的数据保存位置。寄存器可以保存指令、存储地址或进程所需的其他类型的数据。“程序计数器”,也称为“指令指针”,跟踪计算机在其程序序列中的位置。“栈”是一种数据结构,它存储有关计算机程序的活动子例程的信息,并用作进程的暂存空间。它不同于为称为“堆”的进程动态分配的内存。

单个程序可以有多个实例,并且该正在运行的程序的每个实例都是一个进程。每个进程都有单独的内存地址空间,这意味着一个进程独立运行并与其他进程隔离。它不能直接访问其他进程中的共享数据。从一个进程切换到另一个进程(相对地)需要一些时间来保存和加载寄存器、内存映射和其他资源。

进程的这种独立性很有价值,因为操作系统会尽力隔离进程,这样一个进程的问题就不会损坏或破坏另一个进程。毫无疑问,您会遇到这样的情况:您计算机上的一个应用程序冻结或出现问题,而您可以退出该程序而不影响其他应用程序。

线程是进程内的执行单位。一个进程可以只有一个线程,也可以有多个线程。

当进程启动时,会为其分配内存和资源。进程中的每个线程共享该内存和资源。在单线程进程中,进程包含一个线程。进程和线程是同一件事,只有一件事发生。

在多线程进程中,进程包含多个线程,并且该进程同时完成许多事情(从技术上讲,有时几乎是同时完成的-请参阅“并行和并发怎么办?”)中的更多信息。部分)。

我们讨论了进程或线程可用的两种内存类型,堆栈和堆。区分这两种类型的进程内存很重要,因为每个线程都有自己的堆栈,但进程中的所有线程都将共享堆。

线程有时被称为轻量级进程,因为它们有自己的堆栈,但可以访问共享数据。由于线程与进程以及进程内的其他线程共享相同的地址空间,因此线程之间通信的操作成本较低,这是一个优势。缺点是进程中的一个线程出现问题肯定会影响其他线程和进程本身的生存能力。

但是线程更容易受到同一进程中其他线程引起的问题的影响。

线程间通信可能比进程间通信更快,因为同一进程的线程与它们所属的进程共享内存。

您可能会问进程或线程是否可以同时运行。答案是:这要视情况而定。在具有多个处理器或CPU核心的系统上(现代处理器很常见),可以并行执行多个进程或线程。不过,在单个处理器上,不可能真正同时执行进程或线程。在这种情况下,使用进程调度算法在运行的进程或线程之间共享CPU,该算法划分CPU的时间并产生并行执行的假象。分配给每个任务的时间称为“时间片”。任务之间的来回切换发生得如此之快,以至于通常无法察觉。术语并行性(真正的同时执行)和并发(进程的及时交错,以给出同时执行的外观)区分了两种类型的真实或近似的同时操作。

那么,程序员在创建希望同时执行多个任务的程序时,将如何在进程和线程之间做出选择呢?我们已经讨论了上面的一些不同之处,但是让我们通过一个我们很多人都在使用的程序Google Chrome来看一个真实世界的例子。

当谷歌设计Chrome浏览器时,他们需要决定如何同时处理需要计算机、通信和网络资源的许多不同任务。每个浏览器窗口或选项卡与因特网上的多个服务器通信,以检索文本、节目、图形、音频、视频和其他资源,并呈现该数据以供显示和与用户交互。此外,浏览器可以打开许多窗口,每个窗口都有许多任务。

谷歌必须决定如何处理这种任务分离。他们选择将Chrome中的每个浏览器窗口作为单独的进程运行,而不是像其他浏览器一样作为一个线程或多个线程运行。这样做给谷歌带来了很多好处。将每个窗口作为进程运行可保护整个应用程序不受渲染引擎中的错误和故障的影响,并将每个渲染引擎进程的访问限制为其他进程和系统的其余部分。隔离进程中的JavaScript程序可以防止它占用过多的CPU时间和内存,并使整个浏览器无响应。

谷歌对多进程设计进行了精心设计的权衡。与使用线程相比,为每个浏览器窗口启动新进程在内存和资源方面具有更高的固定成本。他们打赌他们的方法最终会减少整体内存膨胀。

使用进程而不是线程还可以在内存不足时提供更好的内存使用率。操作系统将非活动窗口视为较低优先级,并且当其他进程需要内存时,该窗口有资格交换到磁盘。这有助于保持用户可见窗口更具响应性。如果窗口是线程化的,则更难将已使用和未使用的内存完全分开,从而浪费内存和性能。

你可以在谷歌的Chromium博客或Chrome简介漫画上阅读更多关于谷歌对Chrome的设计决定的内容。

下面的屏幕截图显示了在MacBook Air上运行的Google Chrome进程,并打开了许多选项卡。一些Chrome进程占用了相当多的CPU时间和资源,而有些进程使用的非常少。您可以看到,每个进程也有许多线程在运行。

系统上的活动监视器或任务管理器在微调计算机或故障排除问题方面是很有价值的盟友。如果您的计算机运行缓慢,或者某个程序或浏览器窗口有一段时间没有响应,您可以使用系统监视器检查其状态。有时,您会看到标记为“无响应”的进程。尝试退出该进程,看看您的系统是否运行得更好。如果某个应用程序占用大量内存,您可以考虑选择另一个可以完成相同任务的应用程序。

我们希望这篇TRON式的深入研究计算机程序、进程和线程的迷人世界能够帮助您解决一些可能存在的问题。

下次您的计算机运行缓慢或应用程序出现问题时,您知道您的任务。打开系统监视器,看看引擎盖下面发生了什么。现在你说了算。

你还在糊涂吗?有问题吗?如果是这样的话,请在评论中告诉我们。并随时为未来的博客帖子推荐主题。

我添加下面的示例是为了说明当正确使用时,进程或线程如何能够更有效地完成任务。Backblaze最近发布了Backblaze Cloud Backup 5.0版,它将Mac和PC上可用于备份的线程数量翻了一番(最多20个)。在默认设置下,我们的客户端应用程序现在会根据您的环境自动评估什么是最好的,并相应地设置线程数,但您可以手动控制将线程数设置为您想要的任何数量。

下面来自Macintosh的活动监视器的屏幕截图显示,一个系统运行20个线程将数据上传到云中。这个线程数量并不是对所有系统都是最佳的--事实上,在某些系统上,它实际上可能会减慢上传速度。如果有疑问,最好让客户端保持自动线程,让它决定什么对您的系统最好。