初步解析Python下的多进程编程


Posted in Python onApril 28, 2015

要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识。

Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程:

# multiprocessing.py
import os

print 'Process (%s) start...' % os.getpid()
pid = os.fork()
if pid==0:
  print 'I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())
else:
  print 'I (%s) just created a child process (%s).' % (os.getpid(), pid)

运行结果如下:

Process (876) start...
I (876) just created a child process (877).
I am child process (877) and my parent is 876.

由于Windows没有fork调用,上面的代码在Windows上无法运行。由于Mac系统是基于BSD(Unix的一种)内核,所以,在Mac下运行是没有问题的,推荐大家用Mac学Python!

有了fork调用,一个进程在接到新任务时就可以复制出一个子进程来处理新任务,常见的Apache服务器就是由父进程监听端口,每当有新的http请求时,就fork出子进程来处理新的http请求。
multiprocessing

如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?

由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。

multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:

from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
  print 'Run child process %s (%s)...' % (name, os.getpid())

if __name__=='__main__':
  print 'Parent process %s.' % os.getpid()
  p = Process(target=run_proc, args=('test',))
  print 'Process will start.'
  p.start()
  p.join()
  print 'Process end.'

执行结果如下:

Parent process 928.
Process will start.
Run child process test (929)...
Process end.

创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。

join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
Pool

如果要启动大量的子进程,可以用进程池的方式批量创建子进程:

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
  print 'Run task %s (%s)...' % (name, os.getpid())
  start = time.time()
  time.sleep(random.random() * 3)
  end = time.time()
  print 'Task %s runs %0.2f seconds.' % (name, (end - start))

if __name__=='__main__':
  print 'Parent process %s.' % os.getpid()
  p = Pool()
  for i in range(5):
    p.apply_async(long_time_task, args=(i,))
  print 'Waiting for all subprocesses done...'
  p.close()
  p.join()
  print 'All subprocesses done.'

执行结果如下:

Parent process 669.
Waiting for all subprocesses done...
Run task 0 (671)...
Run task 1 (672)...
Run task 2 (673)...
Run task 3 (674)...
Task 2 runs 0.14 seconds.
Run task 4 (673)...
Task 1 runs 0.27 seconds.
Task 3 runs 0.86 seconds.
Task 0 runs 1.41 seconds.
Task 4 runs 1.91 seconds.
All subprocesses done.

代码解读:

对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。

请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。如果改成:

p = Pool(5)

就可以同时跑5个进程。

由于Pool的默认大小是CPU的核数,如果你不幸拥有8核CPU,你要提交至少9个子进程才能看到上面的等待效果。
进程间通信

Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。

我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:

from multiprocessing import Process, Queue
import os, time, random

# 写数据进程执行的代码:
def write(q):
  for value in ['A', 'B', 'C']:
    print 'Put %s to queue...' % value
    q.put(value)
    time.sleep(random.random())

# 读数据进程执行的代码:
def read(q):
  while True:
    value = q.get(True)
    print 'Get %s from queue.' % value

if __name__=='__main__':
  # 父进程创建Queue,并传给各个子进程:
  q = Queue()
  pw = Process(target=write, args=(q,))
  pr = Process(target=read, args=(q,))
  # 启动子进程pw,写入:
  pw.start()
  # 启动子进程pr,读取:
  pr.start()
  # 等待pw结束:
  pw.join()
  # pr进程里是死循环,无法等待其结束,只能强行终止:
  pr.terminate()

运行结果如下:

Put A to queue...
Get A from queue.
Put B to queue...
Get B from queue.
Put C to queue...
Get C from queue.

在Unix/Linux下,multiprocessing模块封装了fork()调用,使我们不需要关注fork()的细节。由于Windows没有fork调用,因此,multiprocessing需要“模拟”出fork的效果,父进程所有Python对象都必须通过pickle序列化再传到子进程去,所有,如果multiprocessing在Windows下调用失败了,要先考虑是不是pickle失败了。
小结

在Unix/Linux下,可以使用fork()调用实现多进程。

要实现跨平台的多进程,可以使用multiprocessing模块。

进程间通信是通过Queue、Pipes等实现的。

Python 相关文章推荐
实践Python的爬虫框架Scrapy来抓取豆瓣电影TOP250
Jan 20 Python
python thrift搭建服务端和客户端测试程序
Jan 17 Python
Python中的defaultdict与__missing__()使用介绍
Feb 03 Python
Python中浅拷贝copy与深拷贝deepcopy的简单理解
Oct 26 Python
Python类的继承用法示例
Jan 31 Python
打包python 加icon 去掉cmd黑窗口方法
Jun 24 Python
Django Form 实时从数据库中获取数据的操作方法
Jul 25 Python
Python获取当前脚本文件夹(Script)的绝对路径方法代码
Aug 27 Python
详解用Python进行时间序列预测的7种方法
Mar 13 Python
python 中的命名空间,你真的了解吗?
Aug 19 Python
Python爬虫模拟登陆哔哩哔哩(bilibili)并突破点选验证码功能
Dec 21 Python
Django集成富文本编辑器summernote的实现步骤
May 31 Python
python实现将pvr格式转换成pvr.ccz的方法
Apr 28 #Python
简单介绍Python中的JSON使用
Apr 28 #Python
浅析Python中的序列化存储的方法
Apr 28 #Python
详解在Python和IPython中使用Docker
Apr 28 #Python
在Python程序中进行文件读取和写入操作的教程
Apr 28 #Python
介绍Python中的文档测试模块
Apr 28 #Python
Django中几种重定向方法
Apr 28 #Python
You might like
php中将地址生成迅雷快车旋风链接的代码[测试通过]
2011/04/20 PHP
php 删除一个数组中的某个值.兼容多维数组!
2012/02/18 PHP
php中文验证码实现示例分享
2014/01/12 PHP
php+xml实现在线英文词典之添加词条的方法
2015/01/23 PHP
Zend Framework教程之模型Model用法简单实例
2016/03/04 PHP
Mootools 1.2教程 Fx.Tween的使用
2009/09/15 Javascript
jQuery中noconflict函数的实现原理分解
2015/02/03 Javascript
简述Jquery与DOM对象
2015/07/10 Javascript
EasyUI Pagination 分页的两种做法小结
2016/07/09 Javascript
Vue表单绑定的实例代码(单选按钮,选择框(单选时,多选时,用 v-for 渲染的动态选项)
2019/05/13 Javascript
vue使用swiper实现中间大两边小的轮播图效果
2019/11/24 Javascript
Vue 实现监听窗口关闭事件,并在窗口关闭前发送请求
2020/09/01 Javascript
从零学python系列之浅谈pickle模块封装和拆封数据对象的方法
2014/05/23 Python
Python3 queue队列模块详细介绍
2018/01/05 Python
Python实现PS滤镜特效Marble Filter玻璃条纹扭曲效果示例
2018/01/29 Python
python使用for循环计算0-100的整数的和方法
2019/02/01 Python
基于wxPython的GUI实现输入对话框(2)
2019/02/27 Python
python使用paramiko模块通过ssh2协议对交换机进行配置的方法
2019/07/25 Python
win10下安装Anaconda的教程(python环境+jupyter_notebook)
2019/10/23 Python
浅析python,PyCharm,Anaconda三者之间的关系
2019/11/27 Python
tensorflow获取预训练模型某层参数并赋值到当前网络指定层方式
2020/01/24 Python
.img/.hdr格式转.nii格式的操作
2020/07/01 Python
python 利用toapi库自动生成api
2020/10/19 Python
HTML5对比HTML4的主要改变和改进总结
2016/05/27 HTML / CSS
利达恒信公司.NET笔试题面试题
2016/03/05 面试题
服装设计专业自荐书范文
2013/12/30 职场文书
美容院经理岗位职责
2014/04/03 职场文书
心理健康活动总结
2014/04/30 职场文书
作风年建设汇报材料
2014/08/14 职场文书
刑事附带民事起诉状
2015/05/19 职场文书
2015年社区反邪教工作总结
2015/10/14 职场文书
干货:如何写好观后感 !
2019/05/21 职场文书
导游词之永济鹳雀楼
2020/01/16 职场文书
python for循环赋值问题
2021/06/03 Python
Python装饰器的练习题
2021/11/23 Python
python中pycryto实现数据加密
2022/04/29 Python