程序,进程和线程有什么区别?

信仰

本文非原创,译自https://www.backblaze.com/blo...
第一次翻译,文中有错误的地方还请指正,谢谢。


想必你多次听说过计算机程序中的“线程”了,但你并不能确定它是什么意思?进程又是怎样的呢?你可能知道线程在某种程度上跟程序和进程是密切相关的,如果你不是计算机科学专业的话,也许这就是你的理解了。

如果你是一名程序员,那么知道这些术语的含义是绝对必要的,但对于一个普通的计算机用户而言了解了它们也是有用的。有助于你使用Mac的活动监视器,Windows的任务管理器,或者Linux的Top(性能分析工具),它可以帮助你定位解决计算机上出现问题的程序,或者是否需要安装更多的内存来使你的系统运行的更好。

让我们花几分钟来探索计算机程序的世界,并弄请这些术语的含义。我们将去简化并概括一些概念,涵盖的这些概念将有助于我们理清这些术语之间的区别。

Programs(程序)

首先,你可能知道程序是存储在计算机上的旨在完特定任务的代码,程序有多种类型,其中包括计算机的函数程序和操作系统部分的程序,以及特定工作的其它程序。这些特定任务的程序也被称为“应用程序”,它包括诸如文本处理程序,web浏览程序,或通过电子邮件将消息发送到另一台计算机的程序。
c# 代码示例

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

最终结果是相同的,因为程序在运行的时候,它是以二进制的形式加载到内存中。计算机的CPU(Central Processing Unit)仅理解二进制指令,因此当CPU运行时程序需要被转换为二进制的形式。

二进制是计算机原生语言,因为电子电路有两种基本状态,打开或者关闭,它们用0和1来表示。在我们日常使用的通用编码系统中,是基于10进制的,每个数位都可以是从0到9的数字。在而二进制中,每个位置可以是0或1。

也许你听过一个关于程序员的笑话,“世界上只有10种人,懂二进制的人和不懂二进制的人”
十进制二进制
00000
10001
20010
30011
40100
50101
60110
70111
81000
91001

进程怎样工作?

程序已经以二进制的形式加载到计算机内存中了。那么接下来呢?

执行中的程序不只是需要告诉计算机做什么的二进制代码。程序还需要内存和各种操作系统资源才能运行。“进程”就是我们说的程序,它和它所需要去操作的所有资源一起被加载到内存中。“操作系统”是分配所有资源的大脑,它有多种类型,诸如macOS,IOS,Microsoft Windows,Linux,以及Android。操作系统负责管理将程序转换为运行中的进程所需的资源。

任何进程都需要一些必要的资源,如寄存器,程序计数器和栈。“寄存器”是计算机处理器(CPU)用于数据存储区域的部分。寄存器可以存放指令,存储地址,或进程需要的其它类型的数据。“程序计数器”也被称为指令指针,它用于指示计算机在其程序序列中的位置。“栈”是一种存储计算机程序的当前子程序信息(这里个人理解为你调试代码时的执行上下文)的数据结构,被用于进程的暂存空间。它跟为进程动态分配的内存(被称为“堆”)是需要区分开来的。

计算机的进程

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

进程的这种独立是很重要的,因为操作系统会尽可能的隔离进程以保证一个进程的问题不会影响到另一个进程。你肯定碰到过计算机一个应用程序卡死或者出现问题的情况,并且你能够在不影响其它程序的情况下退出该程序。

线程如何工作?

你还在跟着我们往下阅读吗?我们终于讲到了线程!

线程是进程的执行单元。一个进程可以有一个或者多个线程。

进程与线程

当一个进程开始时,它会被指定内存和资源。进程中的每个线程共享内存和资源。在单线程进程中,进程只包含一个线程。进程和线程是相同的,并且只有一件事在发生。

在多线程进程中,进程包含多个线程,而且该进程在同时做多个事情(从技术角度而言,有时几乎等于同时——想了解更多参见“并行和并发?”下面的部分)

单线程和多线程

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

线程有时被称为轻量级进程,是由于它们都有自己的栈而且可以访问共享数据。因为线程跟进程以及进程中的其它线程共享相同的地址空间(表示计算机实体所占用的内存大小),两个线程之间的通讯开销就很低,这是有利的。不利的是进程中的一个线程有问题必定会影响到其它线程以及进程本身的存活。

进程 VS 线程

我们来回顾一下:

  1. 程序以编程代码的文本文件开始
  2. 程序会被编译或解释成二进制的形式
  3. 程序会被加载进内存
  4. 程序可以运行一个或多个进程
  5. 进程通常是和其它进程独立开的
  6. 线程作为进程的子集存在
  7. 线程之间通讯比进程之间通讯要容易的多
  8. 线程更容易被相同进程下的其它线程导致的问题影响

线程 vs 进程 —— 优势 和 劣势

线程进程
进程操作是重量级的线程操作是更轻量的
每个进程都有自己的内存空间线程使用它们所属进程的内存
进程之间通讯较慢,因为进程都有不同的内存地址线程之间通讯比进程之间通讯更快,因为统一进程的线程共享它们所属进程的内存
进程之间上下文切换是代价更高的相同进程下的线程上下文切换是代价较低的
进程不与其它进程共享内存线程和同一进程下的其它线程共享内存

并行和并发?

你也许有这样一个疑问,进程或者线程是否能在同时运行?答案是:这要看情况。对于多处理器或CPU内核(与现代处理器一样)的系统,多个进程或线程可以并行的执行。对于单个处理器,它是不可能有多个线程或进程真正的同时执行。在这种情况下,CPU在运行的进程或线程之间共享,使用进程调度算法来划分CPU时间,进而产生并行执行的错觉。分配给每个任务的时间被称为“时间切片”。在任务之间来回切换很快,几乎是无法察觉的。术语并行(真正同时执行)和并发(进程及时的交替执行产生的同时执行的现象),用真正同时执行的操作和接近同时执行的操作来区分。

并行和并发

为什么选择进程而不是线程,或者为什么选择线程而不是进程?

那么 当程序员在创建同时执行多个任务的程序时该怎样去选择进程或线程呢?我们在上面已经概括了它们的差异,现在让我们来看一个真实的例子,我们很多人都在使用的程序——Coogle Chrome。

Google在设计Chrome浏览器的时候,它们必需考虑如何同时处理许多需要计算机、通信和网络资源的不同任务。每个浏览器窗口或者选项卡与网络的多个服务通信去获取文本,程序,图像,音频,视频,以及其它资源,继而渲染这些数据以供显示和与用户交互。除此之外,浏览器可以打开多个窗口,每个窗口都有很多任务。

Google必需考虑如何去处理任务的隔离。他们选择把Chrome中每个浏览窗口作为一个单独的进程去运行,而不是像其它浏览器一样作为一个或多个线程去运行。这样做给Google带来了许多好处。将运行中的每个窗口作为一个进程保护了总体应用程序不被渲染引擎的异常和故障所影响,并限制了每个渲染引擎进程对其他进程和系统其余部分的访问。隔离了进程中的JavaScript程序防止它占用太多的CPU时间和内存从而导致整个浏览器失去响应。

Google仔细权衡了多进程的设计。每个浏览器窗口开始一个新的进程在内存和资源方面比起使用线程有更高的固定开销。但是他们敢说他们的方法最终会减少总体的内存膨胀。

当内存不够的时使用进程替代线程也能提供更好的内存用法。不活跃的窗口会被操作系统视为较低的优先级,并且当其它进程需要内存时有资格被调换到磁盘中去。这有助于保持用户显示的窗口更好的响应。如果窗口使用的是线程,想要完全的分隔开已用和未使用的内存是将会更困难,无疑浪费了内存和性能。

您可以在Google的Chromium blogChrome Introduction Comic上了解更多的Google对Chrome的设计决策。

下面的截图将展示运行在MacBook Air上打开了多个选项卡的Google Chrome进程。有些Chrome进程使用了很多的CPU时间和资源,而有些进程使用的非常少。你可以看到每个进程也有许多线程正在运行。

Google Chrome截图

系统上的活动监视器或任务管理器在微调你的计算机或者定位解决问题时是个不错的帮手。如果你的计算机或程序运行的很慢,或者浏览器窗口在一段时间没有响应,你可以使用系统监视器检查它的状态。有时你将会看到进程被标记为“没有响应”。尝试退出那个进程再看看你的系统是否会运行的更快。如果某个应用程序占用着大量的内存,你就应该考虑选择其它的应用程序来完成这个任务了。

活动监视器


剩余一点附录未完全翻译,有兴趣请移步原文。https://www.backblaze.com/blo...

阅读 429

前行者

1 声望
1 粉丝
0 条评论
你知道吗?

前行者

1 声望
1 粉丝
宣传栏