Skip to content

Commit 0953015

Browse files
author
wangchao
committed
更新
1 parent e790670 commit 0953015

File tree

8 files changed

+83
-46
lines changed

8 files changed

+83
-46
lines changed
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
# 在并行编程通信
22

3-
在并行编程中,workers被送来执行任务,而执行任务常常需要建立通信,以便可以合作解决一个问题。
4-
在大多数情况下,通信以一种可以在workers之间进行数据交换的方式被建立。当说到并行编程,有两种通信方式广为人知:共享状态和消息传递。在下面的章节中,将对这两种方式进行简要描述。
3+
在并行编程中,被派去执行任务的**wokers**通常需要建立沟通,以便合作解决问题。 在大多数情况下,这种通信的建立方式是可以在**worker**之间交换数据。 在**并行编程**方面,有两种通信形式更广为人知:**共享状态****消息传递**。 在以下各节中,将对两者进行简要说明。
54

65
## 理解共享状态
76

8-
在workers中最有名的一种通信方式就是共享状态。分享状态似乎是一个简单的使用,但是这会有许多的陷阱,因为若其中某个进程对共享的资源执行了一项无效的操作会影响到所有其它的进程,从而导致一个不好的结果。这也是使在多台计算机之间进行分布式的程序成为不可能的显而易见的原因
7+
**worker**之间最著名的一种交流方式是**共享状态****共享状态**似乎简单易用,但有很多缺陷,因为其中一个进程对**共享资源**进行的无效操作会影响所有其他进程,从而产生不良结果。 由于显而易见的原因,这也使得程序无法在多台机器之间分发
98

10-
为了说明这一点,我们将使用一个真实的案例。假设你是一个具体的银行的一个客户,而这个银行只用一个收银员。当你去银行,你必须要排队等到轮到你的时候。当你在队列中时,你注意到收银员一次只能为一个顾客服务,而收银员不可能同时为两个顾客提供服务而不出错。电脑运算拥有多种手段来以可控的方式访问数据,如mutex(互斥?)
9+
为了说明这一点,我们将使用一个真实的案例。 假设你是某家银行的客户,而这家银行只有一名出纳员。 当你去银行时,你必须排队等待机会。 进入队列后,您会注意到一次只有一位顾客可以使用收银员,收银员不可能同时接待两位顾客而不会出错。 计算提供了以受控方式访问数据的方法,并且有多种技术,例如**互斥锁**(mutex)
1110

12-
Mutex可以理解为一种特殊的过程变量,表示了访问数据的可靠性等级。也就是说,在真实世界的栗子中,顾客有一个编号,在某一特定的时刻,这个编号将会被激活,然后收银员仅对于这个顾客提供服务。在进程结束时,该名顾客将会释放收银员让其为下一个顾客服务,以此类推
11+
**互斥锁**(Mutex)可以理解为一个特殊的过程变量,**表示访问数据的可用性级别**也就是说,在我们现实生活中的例子中,客户有一个号码,在特定的时刻,这个号码会被激活,收银员将专门为这个客户服务。 在流程结束时,这位顾客将为下一位顾客腾出收银员,依此类推
1312

14-
> 在某些情况下,当程序正在运行时,在一个变量中数据会有一个常数值,数据仅仅以只读的目的被分享。所以访问控制不是必须的,因为永远不会出现完整性问题。
13+
!!! info ""
14+
15+
在某些情况下,程序运行时数据在变量中具有常量值,并且共享数据仅用于读取目的。 因此,访问控制不是必需的,因为它永远不会出现完整性问题。
1516

1617
## 理解信息传递
1718

18-
运用消息传递是为了避免来自共享状态带来的数据访问控制以及同步的问题。消息传递包含一种在运行的进程中进行消息交换的机制。每当我们用分布式架构开发程序的时候,就能见到消息传递的使用,在网络中,消息交换被放在一个重要的位置。Erlang等语言,在它的并行体系结构中,就是使用这个模型来实现通信。由于每次数据交换都复制一份数据的拷贝,因此不会出现并发访问的问题。尽管内存使用看起来比共享内存状态要高,但是这个模型还是有一些优势的。优势如下
19+
当我们旨在避免源自共享状态的数据访问控制和同步问题时使用**消息传递**(Message passing)。 **消息传递**由运行进程中的消息交换机制组成。 每当我们开发具有分布式体系结构的程序时,它都非常常用,其中放置它们的网络中的**消息交换**是必要的。 例如,Erlang 等语言使用此模型在其并行架构中实现通信。 一旦在每次消息交换时都复制了数据,就不可能出现访问并发方面的问题。 尽管内存使用率似乎高于共享内存状态,但使用此模型也有优势。 它们如下
1920

20-
*缺乏数据的一致性访问
21-
*数据即可在本地交换(不同的进程)也能在分布式环境中交换
22-
*不太可能出现扩展性问题,并且允许不同系统相互写作。
23-
*一般来说,据程序员来说易于维护。
21+
*没有数据访问并发.
22+
*消息可以在本地(各种进程)或分布式环境中交换.
23+
*这使得可伸缩性问题发生的可能性降低,并实现了不同系统的互操作性.
24+
*总的来说,按照程序员的方式维护起来很容易.
Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,43 @@
11
# 探索并行化的几种模式
22

3-
当我们试图定义并行系统的主要模式时,有困惑很正常。常常会有人提到并发系统和并行系统,这两个术语看起来像是讲的同一件事。然而实际上有着轻微的差异。
3+
当我们试图定义**并行系统**的主要方案时,有疑惑很正常。常常会有人提到**并发系统****并行系统**,这两个术语看起来像是讲的同一件事。然而实际上有着轻微的差异。
44

5-
在并发程序中,我们有这么一个场景,在这个场景中,一个程序分派几个workers,这些workers争着使用CPU来执行任务。在纷争发生的阶段将被CPU调度器所控制,CPU调度器的功能是决定在一个特定的时刻,哪个worker更适合使用资源。在大多数情况下,CPU调度器执行清理过程的任务太快以至于我们会有伪并行的印象。因此,并发编程是来自并行编程的一种抽象。
5+
## 并发编程
66

7-
> 并发系统允许多个任务争夺同一个CPU
7+
**并发编程**中,我们有一个场景,一个程序派发了几个 workers,这些工人争着使用 CPU 来运行一个任务。 在争执发生的阶段由 CPU 调度器控制,其功能是定义在特定时刻哪个 worker 适合使用资源。 在大多数情况下,**CPU** 调度程序运行进程的排序任务非常快,以至于我们可能会产生伪并行的印象。 因此,**并发编程****并行编程**的抽象
88

9-
下图显示了一个并发程序方案:
9+
!!! info ""
1010

11-
![1](https://github.com/Voidly/Img/blob/master/Parallel%20Programming%20with%20Python/Chapter%201/Concurrent%20programming%20scheme.png?raw=true)
11+
并行系统争夺同一CPU来运行任务
1212

13-
并行编程可以被定义为一种方法,在那个方法中,程序数据创造workers在多核环境中同时执行指定的任务,在这些任务中它们不需要并发的接触CPU。
13+
下图显示了一个**并发编程**方案:
1414

15-
> 并行系统同时的运行任务。
15+
![1](../imgs/1-01.png)
1616

17-
下面的图显示了并行系统的概念:
17+
## 并行编程
1818

19-
![2](https://github.com/Voidly/Img/blob/master/Parallel%20Programming%20with%20Python/Chapter%201/Parallel%20programming%20scheme.png?raw=true)
19+
**并行编程**可以被定义为一种方案,在这种方案中,程序数据创建workers以在多核环境中同时运行特定任务,而无需在他们之间并发访问 CPU。
2020

21-
分布式编程旨在在物理分离的计算机器(节点)之间通过消息交换数据来分享进程
21+
!!! info ""
2222

23-
分布式编程变得越来越受欢迎的原因有很多,下面是被探讨的受欢迎的原因:
23+
并行系统同时运行任务。
2424

25-
***容错性**:由于系统是去中心化的,我们可以分发执行到同一个网络的不同机器,从而执行指定机器的个人维护而不影响整个系统的功能。
26-
***横向扩展**:通常我们可以在分布式系统中增加处理的性能。我们可以在不需要终止正在执行的应用的情况下连接新的设备。
27-
***云计算**:随着硬件成本的降低,我们需要这种业务类型的增长,在这种增长中,我们可以获得巨大的机器集群,这些集群对用户来说以一种合作的方式运行并且以一种透明的方式运行程序。
25+
下面的图显示了**并行编程**的概念:
2826

29-
> 分布式系统在物理隔离的节点上运行任务。
27+
![1](../imgs/1-02.png)
28+
29+
## 分布式编程
30+
31+
**分布式编程**旨在通过在物理上分离的计算机(节点)之间交换数据来共享处理的可能性。
32+
33+
由于多种原因,**分布式编程**变得越来越流行; 他们的探索如下:
34+
35+
***容错性**(Fault-tolerance):由于系统是分散的,我们可以将处理分配给网络中的不同机器,从而在不影响整个系统功能的情况下对特定机器进行单独维护。
36+
***横向扩展**(Horizontal scalability):一般来说,我们可以增加分布式系统的处理能力。 我们可以连接新设备而无需中止正在执行的应用程序。 可以说,与垂直可扩展性相比,它更便宜、更简单。
37+
***云计算**:随着硬件成本的降低,我们需要这类业务的增长,我们可以获得以共同合作方式运行的大型机器集群,并以对用户透明的方式运行程序。
38+
39+
> 分布式系统在物理上分离的节点中运行任务。
3040
3141
下图显示了一个分布式系统的方案:
3242

33-
![3](https://github.com/Voidly/Img/blob/master/Parallel%20Programming%20with%20Python/Chapter%201/Distributed%20programming%20scheme.png?raw=true)
43+
![1](../imgs/1-03.png)
Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,53 @@
11
# 识别并行编程的问题
22

3-
当在并行魔鬼居住的大地上战斗之时,勇敢的键盘战士将会遇到一些经典的问题。当没有经验的程序员使用workers和共享状态相结合时,这些问题时有发生。这些问题将会在下面的小节中进行描述
3+
勇敢的键盘战士在并行编程幽灵居住的土地上作战时可能会遇到一些经典问题。 当没有经验的程序员使用结合了共享状态的 worker 时,许多这些问题会更频繁地发生。 其中一些问题将在以下各节中进行描述
44

55
## 死锁
66

7-
死锁是这么一种情形,有两个或两个以上的workers继续为了资源的释放而无限期的等待,而资源由于某些原因被这一组的一个worker所占用。为了更好的理解,我们将使用另一个真实的案例。想象银行的入口有一个旋转门。顾客A走向了允许他进入银行的一侧,而顾客B试图从旋转门入口的一侧离开银行,这样的话门将被卡在那里,两个顾客哪里都去不了。这种情形在现实中会很搞笑,但是在编程中将会是一个悲剧
7+
**死锁**(Deadlock)是指两个或多个 worker 无限期地等待资源释放的情况,由于某种原因,该资源被同一组的 worker 阻塞。 为了更好地理解,我们将使用另一个真实案例。 想象一下入口处有一扇旋转门的银行。 客户 A 转向一侧,这将允许他进入银行,而客户 B 试图通过这个旋转门的入口侧离开银行,这样两个客户都会被困在推门处,但无处可去。 这种情况在现实生活中会很滑稽,但在编程中会很悲惨
88

9-
> 死锁是这么一个现象,进程都在等待一个释放它们任务出现的情况,而这个情况永远不会出现。
9+
!!! info ""
10+
11+
**死锁**(Deadlock)是一种现象,其中进程等待释放任务条件的发生,但这种情况永远不会发生
1012

1113
## 饥饿
1214

1315
这个问题是由于一个或者多个进程不公平的竞争所引起的副作用,这会花费更多的时间来执行任务。想象有一组进程,A进程正在执行繁重的任务,而且这个任务还有数据处理优先级。现在,想象一下,高优先级的进程A持续不断的占用CPU,而低优先级的进程B将永远没有机会。因此可以说进程B在CPU周期中是饥饿的。
1416

15-
> 饥饿是由于进程排名中差劲的调整策略引起的
17+
> **饥饿**(Starvation)是由于进程排名策略调整不当造成的
1618
1719
## 竞态条件
1820

19-
当一个进程的结果取决于执行结果的顺序,而这个顺序由于缺乏同步机制而被破坏,这个时候我们将面临竞态条件。在大的系统中,它们造成的问题将会难以过滤。举个栗子,有一对夫妇有一个联名的账户,在他们操作之前初始的余额是$100.下表显示了常规的有保护机制、预期的事实顺序以及结果情况:
21+
当一个进程的结果取决于执行结果的顺序,而这个顺序由于缺乏同步机制而被打破时,我们就会面临竞态条件。 它们是由在大型系统中极难过滤的问题引起的。 例如,一对夫妇有一个联名账户; 操作前的初始余额为 100 美元。 下表显示了常规情况,其中有保护机制和预期事实的顺序,以及结果:
22+
23+
!!! info "常规操作而不会出现静态条件"
2024

21-
![1](https://github.com/Voidly/Img/blob/master/Parallel%20Programming%20with%20Python/Chapter%201/Presents%20baking%20operations%20without%20the%20chance%20of%20race%20conditions%20occurrence.png?raw=true)
25+
| 丈夫 | 妻子 | 账户余额(美元) |
26+
| -------- | -------- | ---------------- |
27+
| | | 100 |
28+
| 读取余额 | | 100 |
29+
| 存款20 | | 100 |
30+
| 结束操作 | | 120 |
31+
| | 读取余额 | 120 |
32+
| | 取款10 | 120 |
33+
| | 结束操作 | 110 |
2234

2335
在下表中,有问题的场景出现了。假设账户没有同步机制,并且操作的顺序也和预期不一样。
2436

25-
![1](https://github.com/Voidly/Img/blob/master/Parallel%20Programming%20with%20Python/Chapter%201/Analogy%20to%20balance%20the%20problem%20in%20a%20joint%20account%20and%20race%20conditions.png?raw=true)
37+
!!! info "类比在联合账户和竞争条件下平衡问题"
38+
39+
| 丈夫 | 妻子 | 账户余额(美元) |
40+
| --------------------- | --------------------- | ---------------- |
41+
| | | 100 |
42+
| 读取余额 | | 100 |
43+
| 取款100 | | 100 |
44+
| | 读取余额 | 100 |
45+
| | 取款10 | 100 |
46+
| 结束操作<br/>更新余额 | | 0 |
47+
| | 结束操作<br/>更新余额 | 90 |
48+
49+
由于在操作的顺序中意外的缺乏同步,最终结果存在明显的不一致。并行编程的特征之一是不确定性。无法预见两个 worker 将在什么时候运行,甚至谁先运行。 因此,同步机制必不可少。
2650

27-
由于在操作的顺序中意外的没有同步,最终的结果将会明显不一致。并行编程的一个特点就是不确定性。当两个workers都正在运行或者它们中的一个第一个运行的时候,结果将会是不可预见的。因此同步机制是必不可少的。
51+
!!! info ""
2852

29-
> 不确定性,如果缺乏同步机制,将会导致竞态条件的问题。
53+
**不确定性**(Non-determinism),如果与缺乏同步机制相结合,可能会导致竞争条件问题

0 commit comments

Comments
(0)