线程和进程基础——翻译文

前言

所有的内容均来自:http://www.qnx.com/developers/docs/6.4.1/neutrino/getting_started/s1_procs.html
这是一篇很棒的介绍进程和线程的文章,本文属于自己感兴趣翻译过来的文章,有兴趣的读者也可以去拜读一下原文。

进程和线程基础

在我们开始讨论线程,进程,时间片以及各种神奇的“调度机制”之前,先来建立一个类比。

我首先要做的就是说明线程和进程是如何工作的。我能想到的最好的方式(不涉及实时系统的设计)就是把线程和进程想象成一些实际的情形。

进程就像是一个房子

让我们用一个常规的、日常的物品来模拟进程和线程——房子。

房子实际上是一个容器,具有一定的属性(例如楼面面积,房间数目等等)。

如果你能这样看待,那么你就会发现房子不会自己主动去做任何事——它只是一个被动的物体。这就是进程实际上所担任的角色。我们待会儿会讨论到。

线程就像居住者

住在房子里的人们是活跃的对象——他们使用不同的房间,看电视,做饭,洗澡等等。我们很快就会发现这就是线程的行为模式。

单线程

如果你曾经一个人生活过,那么你就会知道这是一种什么感觉——你知道,你可以在任何时候在家里做任何你想做的事,因为房子里没有其他人。如果你想打开立体声音响,使用洗手间,吃晚餐,随便你,你只要继续做就行了。

多线程

当你把另一个人加入房子时,事情会发生戏剧性的变化。假设你结婚了,所以现在你的配偶也住在那里。你不能在任何一个特定的时间进入洗手间,你需要先检查一下,确保你的配偶不在里面!

如果你有两个负责任的成年人住在一所房子里,一般来说,你可能会对“安全”相当松懈-因为你知道另一个大人会尊重你的空间,不会试图放火烧厨房(故意!)等等。

现在,把几个孩子扔到一起,突然之间事情变得有趣多了。

回到进程和线程

就像房子占据了房地产的一部分一样,进程也会占据一定的内存。就像房子的住户可以自由进入任何他们想要的房间一样,一个进程的线程都可以访问这个内存。如果一个线程分配了一些东西(妈妈出去买了一个游戏),所有其他的线程都能立即访问它(因为它存在于公共地址空间-它在房子里)。同样,如果进程分配内存,那么这个新内存也可以用于所有线程。这里的技巧是,确认内存是否应该对进程中的所有线程都可用。如果是,那么您需要让所有线程同步它们对它的访问。如果不是,那么我们假设它是特定于特定线程的。在这种情况下,因为只有该线程才能访问它,所以我们可以假定不需要同步-线程不会自行启动!

正如我们从日常生活中所知道的,事情并不那么简单。现在我们已经了解了基本特性(要点:所有内容都是共享的),让我们来看看事情变得更有趣的地方,以及为什么。

下图显示了我们将代表线程和进程的方式。进程是圆,表示“容器”概念(地址空间),三个squigley lines(不知道是什么)是线程。你会在书中看到这样的图表。

进程作为线程的容器

相互排斥

如果你想洗个澡,而且有人已经在用浴室,你就得等着。线程是如何处理这个的?

它用的是一种叫做互斥的操作。它几乎意味着你所想的-当涉及到特定的资源时,许多线程是互斥的。

如果你正在洗澡,你想要独占浴室。要做到这一点,你通常会进入浴室并把门从里面锁起来。任何想使用浴室的人都会被锁上的。当你完成任务时,你会打开门,让其他人进入。

这就是线程所做的。线程使用一个名为互斥的对象(相互排斥的缩写)。这个对象就像门上的锁-一旦线程拥有互斥锁,没有其他线程可以获得互斥锁,直到拥有的线程释放(解锁)它。就像门锁一样,等待获得互斥锁的线程将被禁止。

互斥锁和门锁的另一个有趣的相同点是互斥锁实际上是一个“咨询”锁。如果一个线程不符合使用互斥锁的约定,那么保护就没有用了。在我们的房子比喻中,这就像有人通过墙壁闯进厕所,无视了门和锁的约定。

优先级

如果浴室现在锁着,有许多人在等着使用它,那该怎么做?显然,所有的人都坐在外面,等着在浴室里的人出去。真正的问题是,“当门打开时会发生什么?谁能下一个进入?“

你会认为,让等了最长的时间得那个成为下一个是“公平的”。或者,让最年长的人成为下一个是“公平”的。或者最高的。或者最重要的。有许多方法可以确定什么是“公平”。

我们通过线程的两个因素来解决这个问题:优先级和等待长度。

假设两个人同时出现在(上锁的)卫生间的门前。其中一人有一个紧迫的事情(他们开会已经很晚了),而另一个却没有。让那个时间紧迫的人下一次进去,是不是很有道理呢?当然会了。唯一的问题是你如何决定谁更“重要”。这可以通过分配优先级来完成(让我们使用一个像中微子(Neutrino)这样的数字,是最低的可用优先级,255是这个版本中的最高值)。房子里有紧迫事情的人将被给予更高的优先权,而那些不被优先考虑的人将被给予较低的优先权。

和线程一样。线程继承其父线程的调度算法,但可以调用pthread_setschedparam()来更改其调度策略和优先级(如果它有权限这样做)。

如果有多个线程等待,并且互斥锁被解锁,我们将把互斥锁给予等待线程中最高优先级的那个。但是,假设两个人都具有相同的优先级。现在你要做什么?好吧,在这种情况下,让等待最长的人下一个或许是“公平的”。这不仅是“公平的”,而且也是在内核所做的。在一堆线程等待的情况下,我们首先是按优先级等级来决定,其次是等待长度。

互斥量肯定不是我们将遇到的唯一的同步对象。让我们看看其他的。

信号量

让我们从浴室搬到厨房,因为这是一个在社会上可以接受同时容纳一个人以上的地点。在厨房里,你可能不想让每个人都在里面。事实上,你可能想要限制你的厨房里所能容纳的人数(太多的厨师,等等)。

假设你不想同时拥有两个以上的人。你能用互斥体来实现吗?这不是我们定义的。为什么不呢?这实际上是一个非常有趣的问题。让我们把它分成几个步骤。

数量为1的信号量

浴室可以有两种情况中的一种,两种状态相互联系(with two states that go hand-in-hand with each other):

  • 门没有锁,房间里没有人
  • 门是锁着的,房间里有一个人

没有其他的组合是可能的-当房间里没人的时候不能上锁(不然我们怎么解锁?),而且当有人在房间里时不能解锁(他们怎么保证他们的隐私?)。这是一个信号量的示例,其计数为1-最多只能有一个人在该房间,或者一个线程使用信号量。

这里的关键(key)(请原谅双关语)是我们描述锁的方式。在典型的浴室锁中,你可以锁定和解锁它只有从内部-没有外部可访问的钥匙。实际上,这意味着互斥对象的所有权是一个原子操作-在获取互斥锁的过程中,没有可能有其他线程得到它,结果是你一直拥有互斥锁。在我们的房子比喻中,这是不太明显的,因为人类比计算机聪明太多(smarter than ones and zeros)。

数量大于1的信号量

假设我们在厨房安装了传统的钥匙锁。这个锁的工作方式是,如果你有一个钥匙,你可以打开门并进入。任何使用这个锁的人都同意,当他们进去时,他们会立即把门从里面锁起来,这样外面的人总是需要钥匙。

现在,控制我们想要多少人在厨房里-把两个钥匙挂在门外,这就变成了一件简单的事情!厨房总是锁着的。当有人想走进厨房时,他们会发现门外有一把钥匙。如果是的话,他们就带着它,打开厨房的门,进去,用钥匙锁上门。

因为进入厨房的人在厨房的时候一定要带钥匙,所以我们可以通过限制门上的钥匙的数量来直接控制进入厨房的人数。

对于线程,这就需要通过信号量来完成了。“普通”信号量就像一个互斥体一样工作-你要么拥有互斥量,在这种情况下,你可以访问资源,或者你没有,在这种情况下,你没有访问权限。我们刚才在厨房描述的信号量是一个计数信号,它保持计数的记录(线程所允许的keys数量)。

互斥的信号量

我们只是问了一个问题“你能用一个互斥量来做吗?”关于用计数实现一个锁,答案是否定的,反过来呢?我们能用信号量作为互斥量吗?

是的。事实上,在某些操作系统中,这正是他们所做的-他们没有互斥锁,只有信号量!那么,为什么要麻烦互斥锁呢?

要回答这个问题,看看你的洗手间。你的房子的建造者是如何实现“互斥”的?我猜你没有挂在墙上的钥匙!

互斥锁(Mutexes)是一个“特殊用途”信号量。如果您希望一个线程在特定的代码段中运行,那么互斥锁是迄今为止最有效的实现。

结束语

这篇文章真的可谓是生动有趣,把线程和进程用了生活中的比喻来描述,很是深刻。原文中的描述虽然简单,但有些单词还是有些拿不准,我都打出来了,甚至有一些描述有些生硬,如有错误,请指出。


欢迎转载,转载请注明出处!
ID:@我没有三颗心脏
github:wmyskxz
欢迎关注公众微信号:wmyskxz
分享自己的学习 & 学习资料 & 生活
想要交流的朋友也可以加qq群:3382693

你可能感兴趣的:(线程和进程基础——翻译文)