解析Python下的多进程编程

   这篇文章主要介绍了初步解析Python下的多进程编程,使用多进程编程一直是Python编程当中的重点和难点,需要的朋友可以参考下

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

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

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

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

  ?

1
2
3
4
5
6
7
8
9

# 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)

  运行结果如下:

  ?

1
2
3

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类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14

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.'

  执行结果如下:

  ?

1
2
3
4

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

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

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

  Pool

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

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

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.'

  执行结果如下:

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13

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有意设计的限制,并不是操作系统的限制。如果改成:

  ?

1

p = Pool(5)

  就可以同时跑5个进程。

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

  进程间通信

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

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

  ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

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()

  运行结果如下:

  ?

1
2
3
4
5
6

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等实现的。

时间: 2024-08-02 10:05:21

解析Python下的多进程编程的相关文章

从Python的源码来解析Python下的freeblock

  这篇文章主要介绍了从Python的源码来解析Python下的freeblock,包括内存空间分配等知识,需要的朋友可以参考下 1 引言 在python内存管理中,有一个block的概念.它比较类似于SGI次级空间配置器. 首先申请一块大的空间(4KB),然后把它切割成一小份(8, 16 一直到512). 当有内存申请的请求时候,简单的流程是:根据大小找到对应的block,然后在freeblock 上给它一份. 2 问题 整个过程是一种比较自然的slab分配方式.但当我读到这段代码时,却感到疑

在Python下尝试多线程编程

  这篇文章主要介绍了在Python下多线程编程的尝试,由于GIL的存在,多线程在Python开发领域一直是个热门问题,需要的朋友可以参考下 多任务可以由多进程完成,也可以由一个进程内的多线程完成. 我们前面提到了进程是由若干线程组成的,一个进程至少有一个线程. 由于线程是操作系统直接支持的执行单元,因此,高级语言通常都内置多线程的支持,Python也不例外,并且,Python的线程是真正的Posix Thread,而不是模拟出来的线程. Python的标准库提供了两个模块:thread和thr

简单介绍利用TK在Python下进行GUI编程的教程_python

我想要向您介绍能想像到的开始 GUI 编程的最简单方法,就是使用 Scriptics 的 TK 和 Tkinter 封装器.我们将与 developerWorks 中的 "Python 中的 curses 编程" 提到的 curses 库进行很多比较.除了 curses 实现文本控制台而 TK 实现 GUI 这一差别之外,这两个库有着惊人相似的接口.在使用任何一个库之前,需要基本了解窗口和事件循环,并参考可用的窗口小部件.(好,好的参考和适量的练习.) 如同关于 curses 的文章,

Python多进程编程下线程之间变量的共享问题

  这篇文章主要介绍了探究Python多进程编程下线程之间变量的共享问题,多进程编程是Python学习进阶中的重要知识,需要的朋友可以参考下 1.问题: 群中有同学贴了如下一段代码,问为何 list 最后打印的是空值? ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from multiprocessing import Process, Manager import os   manage

并发编程下多线程多进程的应用场景

问题描述 并发编程下多线程多进程的应用场景 实现高并发程序时,多线程编程,或者多进程编程它们各自的优势是什么,即它们的应用场景, 解决方案 多线程程序可以更有效利用cpu等资源,但是线程共享相同的内存地址,所以必须严格考虑同步和数据访问的共享问题.多进程可以任意部署到单个计算机甚至多个计算机,可获得更好的伸缩性. 解决方案二: 多线程or多进程. 这主要取决于你的应用场景. 你的应用场景是否支持你使用多进程的方式.比如你的应用是需要从一个消息队列里面取出消息来快速处理,并且这些消息之间还是有相应

Python多进程编程技术实例分析_python

本文以实例形式分析了Python多进程编程技术,有助于进一步Python程序设计技巧.分享给大家供大家参考.具体分析如下: 一般来说,由于Python的线程有些限制,例如多线程不能充分利用多核CPU等问题,因此在Python中我们更倾向使用多进程.但在做不阻塞的异步UI等场景,我们也会使用多线程.本篇文章主要探讨Python多进程的问题. Python在2.6引入了多进程的机制,并提供了丰富的组件及api以方便编写并发应用.multiprocessing包的组件Process, Queue, P

实例解析IPv6环境下的网络编程

自IPv4诞生至今已有20多年了,目前它虽仍因互联网的成功而风光无限,但是如同"Internet正在成为其自身巨大成功的受害者"一样,目前IPv4地址的极度匮乏注定它将被历史所淘汰.而IPv6-IPv4的继承人,具有地址空间巨大,支持QOS等许多优良特性,在不久的将来会迅速的普及,但IPv6的出现将对目前网络编程方式产生一定的影响. 本文将就IPv6环境下的网络编程方式进行实例解析. 最终效果: 实例解析IPv6环境下的网络编程-配置篇 目前我们所用的IP协议是v4版本的, 比如192

Linux下多进程编程(C语言)

Linux下多进程编程(C语言) 一.    进程简介 1.进程是程序的执行.程序是静态的,进程是动态的. 2.进程在内存中有三部分组成:数据段.堆栈段和代码段.          代码段:就是存放程序代码的数据,如果有数个进程运行同一个一个程序,那么它们就可以使用同一个代码段(代码段是可以共享的):          堆栈段:存放的是子程序的返回地址.参数以及程序的局部变量,主要是保存进程的执行的环境,这里用到了栈先进后出的特性,可以看做具有记忆上一次执行的环境.          数据段:存

PHP多进程编程总结(推荐)_php实例

1. 准备 在动手之前,请确定你用的不是M$ Windows平台(因为我没有Windows).Linux / BSD / Unix应该都是没问题的.确认好了工作环境以后一起来看看我们需要的PHP模块是否都有.打开终端输入下面的命令: $ php -m 这个命令检查并打印当前PHP所有开启的扩展,看一下pcntl和posix是否在输出的列表中. 1.1. pcntl 如果找不到pcntl,八成是编译的时候没把这个扩展编译进去.如果你和我一样是编译安装的PHP,那么需要重新编译安装PHP.在配置的时