Skip to content

Commit 8433ff0

Browse files
committed
chapter6前三节
1 parent 94d6caf commit 8433ff0

File tree

4 files changed

+118
-7
lines changed

4 files changed

+118
-7
lines changed

第六章/ReadMe.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
##Utilizing Parallel Python
2-
之前的章节,我们学习了怎么使用multiprocessing模块和ProcessPoolExecutor模块解决两个问题这章将介绍管道和如何使用Parallel Python(PP)利用多进程执行并行任务
2+
之前的章节,我们学习了怎么使用multiprocessing模块和ProcessPoolExecutor模块来解决两个问题.这章将介绍命名管道并展示如何使用Parallel Python(PP)模块实现多进程的并行任务
33
本章会覆盖下面几个知识点:
44
* 理解进程间通信
5-
* 学习Parallel Python(PP)
5+
* 了解Parallel Python(PP)
66
* 在SMP架构上使用PP计算斐波那契序列
7+
* 使用PP创建分布式的网络爬虫

第六章/了解pp模块.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
##了解PP模块
2+
上一章中,我们介绍了直接使用系统调用创建进程间通讯的方法,这是一种很低层的机制. 并且它只在Linux或Unix环境下才有效. 接下来我们会使用一个名为PP的python模块来创建IPC通讯,这种通讯不仅仅可以在本地进程间进行,还能通过计算机网络在物理上彼此分散的进程间进程.
3+
4+
关于PP模块的文档并不多,可以在http://www.parallelpython.com/component/option.com_smf/ 中找到相关文档和FAQ. API提供了大量的使用该工具的说明. 十分的简洁明了
5+
6+
使用PP模块的最大优势在于它提供了一个抽象层. PP模块的重要特征如下所示:
7+
- 自动探测进程的数量并以此改进负载均衡
8+
- 可以在运行期改变要投入的处理器数量
9+
- 运行期自动均衡负载
10+
- 通过网络自动探测资源
11+
12+
PP模块有两种方式来执行并行代码. 第一种方式基于SMP架构,即在同一台机器上有多个处理器/核心. 第二中方式将网络中的各个机器配置,整合成区块,并将任务分配給这些机器去运行. 无论哪一种方式,进程间消息交换的过程都是抽象的. 这使得我们无需关系其低层的实现方式到底是通过管道还是socket. 我们只需要使用回调函数来通过参数和函数的方式来交换信息就行了. 下面给个例子.
13+
14+
在PP的API中有一个名为Server的类,使用该类可以实现在本地和远程的进程间封装和分派任务. Server的构造函数(\_\_init\_\_)中有几个参数比较重要:
15+
16+
* ncpus: 该参数用于指定执行任务的工作进程数量. 若没有指定该参数,则会自动根据机器上处理器/核心的数量来创建工作进程,以后话资源的使用
17+
18+
* ppservers: 该参数是一个元组,该元组的元素为并行Python执行服务器(PPES)的名称或IP地址. 一个PPES由连入网络的机器组成. 且该机器通过ppsever.py共组运行并等待待执行的任务.
19+
20+
其他参数的说明请参阅http://www.parallelpython.com/content/view/15/30/
21+
22+
Server对象的实例拥有很多方法,其中submit方法允许我们分配任务到各个工作进程. submit函数具有如下签名:
23+
```python
24+
submit(self, func, args=(), depfuncs=(), modules=(),
25+
callback=None, callbackargs=(), group='default',
26+
globals=None)
27+
```
28+
29+
在submit方法中,我们集中关注以下几个参数:
30+
* func: 该函数会被本地进程或远程服务器执行
31+
* args: 该参数提供了了执行func函数时的参数
32+
* modules: 该参数说明远程代码(remote code)或进程为了调用func函数,需要导入哪些模块. 例如若被分配的函数用到了time模块,则modules=('time',)
33+
* callback :执行完func后的回调函数,func的执行结果会作为其函数参数. 常用于对func的执行结果作进一步加工
34+
35+
还有其他的参数将会在下一张分析代码时进行说明.

第六章/发现pp模块.md

Whitespace-only changes.

第六章/理解进程间通信.md

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,81 @@
1-
IPC是一种允许多进程间通信的一种机制。
2-
有许多IPC的实现方式,他们依赖于不同架构的运行环境。运行在同一台机器上的进程可以由多种进程间通信方式,比如共享内存、消息队列和管道。如果多进程处于物理上分布式集群上上,我们可以使用RPC的方式。
3-
第五章中我们介绍了Multiprocessing和ProcessPoolExecutor模块,学习了常规的管道的用法。我们学习了共有一个父进程的各个子进程间通信方式,但是有时候我们需要在不相关的进程间传递信息(子进程不共有同一个父进程),我们会想,通过不相关进程的地址空间,是否能够在它们之间建立通信。虽然一个进程不能获取另外一个进程的地址空间,但是我们需要使用一种新的机制——命名管道。
1+
##理解进程间通讯
2+
IPC是允许多进程间通信的一种机制。
3+
有许多IPC的实现方式,他们依赖于不同架构的运行环境。运行在同一台机器上的进程可以由多种进程间通信方式,比如共享内存、消息队列和管道。如果多进程处于物理上分布式集群上上,我们可以使用RPC的方式。
4+
第五章中我们介绍了Multiprocessing和ProcessPoolExecutor模块,学习了常规的管道的用法。我们学习了同一个父进程的各个子进程间通信方式,但是有时候我们需要在不相关的进程间传递信息(子进程不共有同一个父进程),我们会想,通过不相关进程的地址空间,是否能够在它们之间建立通信。然而一个进程不能获取另外一个进程的地址空间,因此我们需要使用一种新的机制——命名管道。
5+
46
###探索命名管道
5-
对于POSIX系统,例如linux,我们可以把所有归结为文件,我们每操作一个文件,我们可以找到一个文件与之对应,我们还可以找到另外一个文件用于描述这个任务,该文件使得我们可以操控任务对应的文件。
6-
命名管道通过使用文件描述符(该文件描述符对应于要执行的任务,比如FIFO方式读写文件任务)的方式实现进程间IPC通信。在对信息的管理上命名管道不同于常规管道,
7+
对于POSIX系统,例如linux,我们可以把所有东西归结为文件,我们每操作一个任务,我们可以找到一个文件与之对应,同时还有一个文件描述符与该文件相联系,通过文件描述符就可以操作该文件.
8+
9+
命名管道是一种允许通过某些特殊文件的文件描述符实现IPC通讯的机制.这些特殊文件使用特殊的模式(例如先入先出)来读写数据.在对信息的管理上命名管道不同于常规管道,命名管道通过文件系统中的特殊文件及其文件描述符来实现,而普通管道是在内存中创建的.
10+
11+
###在python中使用命名管道
12+
在python中使用命名管道很容易. 下面我们会通过两个非直接通讯的程序来展示如何使用命名管道. 第一个程序名为write\_to\_named_pip.py,它的作用是写一条22个字节的消息到管道中,该消息包括一个信息字符串及产生消息的PID. 第二个程序名为read\_from\_named\_pip.py, 它会从管道中读取消息并展示消息内容及其PID.
13+
14+
在执行的最后,read\_from\_named\_pipe.py进程会显示一条形如"I pid [<The PID of reader process>] received a message => Hello from pid [the PID of writer process"的消息
15+
16+
为了展示读写进程的相互依赖关系,我们会在两个独立的控制台上运行这两个程序. 在展示结果前,让我们先分析一下两个程序的代码.
17+
18+
####往命名管道写入数据
19+
在python中,命名管道是通过系统调用来实现的. 下面我们会逐行解释write\_to\_named\_pipe.py中代码的功能.
20+
21+
我们首先导入os模块,这样我们在后面的代码中才能调用系统调用.
22+
```python
23+
import os
24+
```
25+
26+
接下来我们会解释\_\_main\_\_代码块,在该代码块中创建了命名管道以及一个用于存储消息的FIFO的特殊文件. \_\_main\_\_代码块中的第一行代码定义了命名管道的标签.
27+
```python
28+
named_pipe = "my_pipe"
29+
```
30+
31+
接下来我们检查该命名管道是否已经存在,若不存在,则调用mkfifo系统调用来创建这个命名管道.
32+
```python
33+
if not os.path.exists(named_pipe):
34+
os.mkfifo(named_pipe)
35+
```
36+
37+
mkfifo调用会创建一个特殊的文件,该文件对通过命名管道读写的消息实现了FIFO机制.
38+
39+
我们再以一个命名管道和一个行如"Hello from pid [%d]"的消息来作为参数调用函数write\_message. 该函数会将消息写入到(作为参数传递给它的)命名管道所代表的文件中. write\_message函数定义如下:
40+
```python
41+
def write_message(input_pipe, message):
42+
fd = os.open(input_pipe, os.O_WRONLY)
43+
os.write(fd, (message % str(os.getpid())))
44+
os.close(fd)
45+
```
46+
47+
我们可以观察到,在函数的第一行,我们调用一个系统调用:open. 该系统调用若成功的话会返回一个文件描述符, 通过该文件描述符我们就能够读写那个FIFO文件中的数据. 请注意,我们可以通过flags参数控制打开FIFO文件的模式. 由于write\_message函数紧紧需要写数据,因此我们使用如下代码:
48+
```python
49+
fd = os.open(input_pipe, os.O_WRONLY)
50+
```
51+
52+
在成功打开命名管道后,我们使用下面代码写入消息:
53+
```python
54+
os.write(fd, (message % os.getpid()))
55+
```
56+
57+
最后,请一定记着使用close关闭通讯渠道,这样才能释放被占用的计算机资源.
58+
```python
59+
os.close(fd)
60+
```
61+
62+
####从命名管道读取数据
63+
我们实现了read\_from\_pipe.py来读取命名管道. 当然,改程序也需要借组os模块才能操作命名模块. 改程序的主要代码很简单:首先,我们定义了所使用命名管道的标签,该标签需要与写进程所用的命名管道同名.
64+
```python
65+
named_pipe = "my_pipe"
66+
```
67+
68+
然后,我们调用read\_message函数,该函数会读取write\_to\_named\_pipe.py写入的内容. read\_message函数的定义如下:
69+
```python
70+
def read_message(input_type):
71+
fd = os.open(input_pipe, os_RONLY)
72+
message = (
73+
"I pid [%d] received a message => %s"
74+
% (os.getpid(), os.read(fd, 22))
75+
os.close(fd)
76+
return message
77+
```
78+
79+
上面代码中,open调用相比无需再介绍了,唯一没见过的只有read调用. 该调用从命名管道中读取指定字节的内容. 这里我们从文件描述符中读取22个字节的内容. 消息被读取出来之后,又被该函数作为返回值返回. 最后依然记得调用close调用来关闭通讯渠道.
80+
81+
最终,下面的截屏显示了write\_to\_named\_pip和read\_from\_named\_pipe程序的执行结果.

0 commit comments

Comments
 (0)