Skip to content

Commit cc3f74b

Browse files
committed
add Process.md
1 parent 8c1118a commit cc3f74b

1 file changed

Lines changed: 225 additions & 0 deletions

File tree

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,227 @@
11
# 进程
22

3+
**进程(process)是正在运行的程序的实例,但一个程序可能会产生多个进程**。比如,打开 Chrome 浏览器程序,它可能会产生多个进程,主程序需要一个进程,一个网页标签需要一个进程,一个插件也需要一个进程,等等。
4+
5+
每个进程都有自己的地址空间,内存,数据栈以及其他记录其运行状态的辅助数据,不同的进程只能使用消息队列、共享内存等进程间通讯(IPC)方法进行通信,而不能直接共享信息。
6+
7+
# fork()
8+
9+
在介绍 Python 的进程编程之前,让我们先看看 Unix/Linux 中的 `fork` 函数。在 Unix/Linux 系统中,`fork` 函数被用于创建进程。这个函数很特殊,对于普通的函数,调用它一次,返回一次,但是调用 `fork` 一次,它返回两次。事实上,`fork` 函数创建了新的进程,我们把它称为子进程,子进程几乎是当前进程(即父进程)的一个拷贝:它会复制父进程的代码段,堆栈段和数据段。
10+
11+
对于父进程,`fork` 函数返回了子进程的进程号 pid,对于子进程,`fork` 函数则返回 `0`,这也是 `fork` 函数返回两次的原因,根据返回值,我们可以判断进程是父进程还是子进程。
12+
13+
下面我们看一段 C 代码,它展示了 fork 的基本使用:
14+
15+
```c
16+
#include <unistd.h>
17+
#include <stdio.h>
18+
19+
int main(int argc, char const *argv[])
20+
{
21+
int pid;
22+
pid = fork(); // 使用 fork 函数
23+
24+
if (pid < 0) {
25+
printf("Fail to create process\n");
26+
}
27+
else if (pid == 0) {
28+
printf("I am child process (%d) and my parent is (%d)\n", getpid(), getppid());
29+
}
30+
else {
31+
printf("I (%d) just created a child process (%d)\n", getpid(), pid);
32+
}
33+
return 0;
34+
}
35+
```
36+
37+
其中,`getpid` 用于获取当前进程号,`getppid` 用于获取父进程号。
38+
39+
事实上,Python 的 os 模块包含了普遍的操作系统功能,该模块也提供了 `fork` 函数,把上面的代码改成用 Python 来实现,如下:
40+
41+
```python
42+
import os
43+
44+
pid = os.fork()
45+
46+
if pid < 0:
47+
print 'Fail to create process'
48+
elif pid == 0:
49+
print 'I am child process (%s) and my parent is (%s).' % (os.getpid(), os.getppid())
50+
else:
51+
print 'I (%s) just created a child process (%s).' % (os.getpid(), pid)
52+
```
53+
54+
运行上面的代码,产生如下输出:
55+
56+
```
57+
I (86645) just created a child process (86646).
58+
I am child process (86646) and my parent is (86645).
59+
```
60+
61+
需要注意的是,虽然子进程复制了父进程的代码段和数据段等,但是一旦子进程开始运行,子进程和父进程就是相互独立的,它们之间不再共享任何数据。
62+
63+
# 多进程
64+
65+
Python 提供了一个 [multiprocessing][mp] 模块,利用它,我们可以来编写跨平台的多进程程序。
66+
67+
我们先来看一个简单的例子,该例子演示了在主进程中启动一个子进程,并等待其结束,代码如下:
68+
69+
```python
70+
import os
71+
from multiprocessing import Process
72+
73+
# 子进程要执行的代码
74+
def child_proc(name):
75+
print 'Run child process %s (%s)...' % (name, os.getpid())
76+
77+
if __name__ == '__main__':
78+
print 'Parent process %s.' % os.getpid()
79+
p = Process(target=child_proc, args=('test',))
80+
print 'Process will start.'
81+
p.start()
82+
p.join()
83+
print 'Process end.'
84+
```
85+
86+
在上面的代码中,我们从 multiprocessing 模块引入了 Process,Process 是一个用于创建进程对象的类,其中,target 指定了进程要执行的函数,args 指定了参数。在创建了进程实例 p 之后,我们调用 start 方法开始执行该子进程,接着,我们又调用了 join 方法,该方法用于阻塞子进程以外的所有进程(这里指父进程),当子进程执行完毕后,父进程才会继续执行,它通常用于进程间的同步。
87+
88+
可以看到,用上面这种方式来创建进程比直接使用 `fork` 更简单易懂。现在,让我们看下输出结果:
89+
90+
```
91+
Parent process 7170.
92+
Process will start.
93+
Run child process test (10075)...
94+
Process end.
95+
```
96+
97+
## 使用进程池创建多个进程
98+
99+
在上面,我们只是创建了一个进程,如果要创建多个进程呢?Python 提供了**进程池**的方式,让我们批量创建子进程,让我们看一个简单的示例:
100+
101+
```python
102+
import os, time
103+
from multiprocessing import Pool
104+
105+
def foo(x):
106+
print 'Run task %s (pid:%s)...' % (x, os.getpid())
107+
time.sleep(2)
108+
print 'Task %s result is: %s' % (x, x * x)
109+
110+
if __name__ == '__main__':
111+
print 'Parent process %s.' % os.getpid()
112+
p = Pool(4) # 设置进程数
113+
for i in range(5):
114+
p.apply_async(foo, args=(i,)) # 设置每个进程要执行的函数和参数
115+
print 'Waiting for all subprocesses done...'
116+
p.close()
117+
p.join()
118+
print 'All subprocesses done.'
119+
```
120+
121+
在上面的代码中,Pool 用于生成进程池,对 Pool 对象调用 apply_async 方法可以使每个进程异步执行任务,也就说不用等上一个任务执行完才执行下一个任务,close 方法用于关闭进程池,确保没有新的进程加入,join 方法会等待所有子进程执行完毕。
122+
123+
看看执行结果:
124+
125+
```python
126+
Parent process 7170.
127+
Run task 1 (pid:10320)...
128+
Run task 0 (pid:10319)...
129+
Run task 3 (pid:10322)...
130+
Run task 2 (pid:10321)...
131+
Waiting for all subprocesses done...
132+
Task 1 result is: 1
133+
Task 0 result is: 0
134+
Run task 4 (pid:10320)...
135+
Task 3 result is: 9
136+
Task 2 result is: 4
137+
Task 4 result is: 16
138+
All subprocesses done.
139+
```
140+
141+
# 进程间通信
142+
143+
进程间的通信可以通过管道(Pipe),队列(Queue)等多种方式来实现。Python 的 multiprocessing 模块封装了底层的实现机制,让我们可以很容器地实现进程间的通信。
144+
145+
下面以队列(Queue)为例,在父进程中创建两个子进程,一个往队列写数据,一个从对列读数据,代码如下:
146+
147+
```python
148+
# -*- coding: utf-8 -*-
149+
150+
from multiprocessing import Process, Queue
151+
152+
# 向队列中写入数据
153+
def write_task(q):
154+
try:
155+
n = 1
156+
while n < 5:
157+
print "write, %d" % n
158+
q.put(n)
159+
time.sleep(1)
160+
n += 1
161+
except BaseException:
162+
print "write_task error"
163+
finally:
164+
print "write_task end"
165+
166+
# 从队列读取数据
167+
def read_task(q):
168+
try:
169+
n = 1
170+
while n < 5:
171+
print "read, %d" % q.get()
172+
time.sleep(1)
173+
n += 1
174+
except BaseException:
175+
print "read_task error"
176+
finally:
177+
print "read_task end"
178+
179+
if __name__ == "__main__":
180+
q = Queue() # 父进程创建Queue,并传给各个子进程
181+
182+
pw = Process(target=write_task, args=(q,))
183+
pr = Process(target=read_task, args=(q,))
184+
185+
pw.start() # 启动子进程 pw,写入
186+
pr.start() # 启动子进程 pr,读取
187+
pw.join() # 等待 pw 结束
188+
pr.join() # 等待 pr 结束
189+
print "DONE"
190+
```
191+
192+
执行结果如下:
193+
194+
```python
195+
write, 1
196+
read, 1
197+
write, 2
198+
read, 2
199+
write, 3
200+
read, 3
201+
write, 4
202+
read, 4
203+
write_task end
204+
read_task end
205+
DONE
206+
```
207+
208+
# 小结
209+
210+
- 进程是正在运行的程序的实例。
211+
- 由于每个进程都有各自的内存空间,数据栈等,所以只能使用进程间通讯(Inter-Process Communication, IPC),而不能直接共享信息。
212+
- Python 的 multiprocessing 模块封装了底层的实现机制,让我们可以更简单地编写多进程程序。
213+
214+
# 参考资料
215+
216+
- [多进程 - 廖雪峰的官方网站](http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0013868323401155ceb3db1e2044f80b974b469eb06cb43000)
217+
- [Linux下Fork与Exec使用 - hicjiajia - 博客园](http://www.cnblogs.com/hicjiajia/archive/2011/01/20/1940154.html)
218+
- [Python 中的进程、线程、协程、同步、异步、回调 - 七牛云存储 - SegmentFault](https://segmentfault.com/a/1190000001813992)
219+
- [编程中的进程、线程、协程、同步、异步、回调 · 浮生半日闲](https://wangdashuaihenshuai.github.io/2015/10/17/%E7%BC%96%E7%A8%8B%E4%B8%AD%E7%9A%84%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E3%80%81%E5%8D%8F%E7%A8%8B%E3%80%81%E5%90%8C%E6%AD%A5%E3%80%81%E5%BC%82%E6%AD%A5%E3%80%81%E5%9B%9E%E8%B0%83/)
220+
- [python中多进程以及多线程编程的总结 - Codefly](http://sunms.codefly.top/2016/11/05/python%E4%B8%AD%E5%A4%9A%E8%BF%9B%E7%A8%8B%E4%BB%A5%E5%8F%8A%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B%E7%9A%84%E6%80%BB%E7%BB%93/)
221+
- [multithreading - Python multiprocessing.Pool: when to use apply, apply_async or map? - Stack Overflow](http://stackoverflow.com/questions/8533318/python-multiprocessing-pool-when-to-use-apply-apply-async-or-map)
222+
223+
224+
225+
[mp]: https://docs.python.org/2/library/multiprocessing.html
226+
227+

0 commit comments

Comments
 (0)