multiprocessing包是Python中的多进程管理包。

与threading.Thread类似,它可以利用multiprocessing.Process对象来创建一个进程。

该进程可以运行在Python程序内部编写的函数。

该Process对象与Thread对象的用法相同,也有start(), run(), join()的方法。

此外multiprocessing包中也有Lock/Event/Semaphore/Condition类 (这些对象可以像多线程那样,通过参数传递给各个进程),用以同步进程,其用法与threading包中的同名类一致。

所以,multiprocessing的很大一部份与threading使用同一套API,只不过换到了多进程的情境。

但在使用这些共享API的时候,我们要注意以下几点:

  • 在UNIX平台上,当某个进程终结之后,该进程需要被其父进程调用wait,否则进程成为僵尸进程(Zombie)。所以,有必要对每个Process对象调用join()方法 (实际上等同于wait)。对于多线程来说,由于只有一个进程,所以不存在此必要性。
  • multiprocessing提供了threading包中没有的IPC(比如Pipe和Queue),效率上更高。应优先考虑Pipe和Queue,避免使用Lock/Event/Semaphore/Condition等同步方式 (因为它们占据的不是用户进程的资源)。
  • 多进程应该避免共享资源。在多线程中,我们可以比较容易地共享资源,比如使用全局变量或者传递参数。在多进程情况下,由于每个进程有自己独立的内存空间,以上方法并不合适。此时我们可以通过共享内存和Manager的方法来共享资源。但这样做提高了程序的复杂度,并因为同步的需要而降低了程序的效率。

Process.PID中保存有PID,如果进程还没有start(),则PID为None

我们可以从下面的程序中看到Thread对象和Process对象在使用上的相似性与结果上的不同。各个线程和进

程都做一件事:打印PID。但问题是,所有的任务在打印的时候都会向同一个标准输出(stdout)输出。这样输出的字符会混合在一起,无法阅读。使用Lock同步,在一个任务输出完成之后,再允许另一个任务输出,可以避免多个任务同时向终端输出

import os
import threading
import multiprocessing # Main
print('Main:', os.getpid()) # worker function
def worker(sign, lock):
lock.acquire()
print(sign, os.getpid())
lock.release() # Multi-thread
record = []
lock = threading.Lock() # Multi-process
record = []
lock = multiprocessing.Lock() if __name__ == '__main__':
for i in range(5):
thread = threading.Thread(target=worker, args=('thread', lock))
thread.start()
record.append(thread) for thread in record:
thread.join() for i in range(5):
process = multiprocessing.Process(target=worker, args=('process', lock))
process.start()
record.append(process) for process in record:
process.join()
Main: 10012
thread 10012
thread 10012
thread 10012
thread 10012
thread 10012
Main: 6052
process 6052
Main: 8080
Main: 4284
Main: 7240
process 8080
process 4284
process 7240
Main: 10044
process 10044

Pipe和Queue

正如我们在Linux多线程中介绍的管道PIPE和消息队列message queue,multiprocessing包中有Pipe类和Queue类来分别支持这两种IPC机制。Pipe和Queue可以用来传送常见的对象。

  1. Pipe可以是单向(half-duplex),也可以是双向(duplex)。我们通过mutiprocessing.Pipe(duplex=False)创建单向管道 (默认为双向)。一个进程从PIPE一端输入对象,然后被PIPE另一端的进程接收,单向管道只允许管道一端的进程输入,而双向管道则允许从两端输入。

下面的程序展示了Pipe的使用:

import multiprocessing as mul

def proc1(pipe):
pipe.send('hello')
print('proc1 rec:', pipe.recv()) def proc2(pipe):
print('proc2 rec:', pipe.recv())
pipe.send('hello, too') # Build a pipe
pipe = mul.Pipe()
if __name__ == '__main__':
# Pass an end of the pipe to process 1
p1 = mul.Process(target=proc1, args=(pipe[0],))
# Pass the other end of the pipe to process 2
p2 = mul.Process(target=proc2, args=(pipe[1],))
p1.start()
p2.start()
p1.join()
p2.join()
proc2 rec: hello
proc1 rec: hello, too

这里的Pipe是双向的。

Pipe对象建立的时候,返回一个含有两个元素的表,每个元素代表Pipe的一端(Connection对象)。我们对Pipe的某一端调用send()方法来传送对象,在另一端使用recv()来接收。

  1. Queue与Pipe相类似,都是先进先出的结构。但Queue允许多个进程放入,多个进程从队列取出对象。Queue使用mutiprocessing.Queue(maxsize)创建,maxsize表示队列中可以存放对象的最大数量。

下面的程序展示了Queue的使用:

import os
import multiprocessing
import time
#==================
# input worker
def inputQ(queue):
info = str(os.getpid()) + '(put):' + str(time.time())
queue.put(info) # output worker
def outputQ(queue,lock):
info = queue.get()
lock.acquire()
print (str(os.getpid()) + ' get: ' + info)
lock.release()
#===================
# Main
record1 = [] # store input processes
record2 = [] # store output processes
lock = multiprocessing.Lock() # To prevent messy print
queue = multiprocessing.Queue(3) if __name__ == '__main__':
# input processes
for i in range(10):
process = multiprocessing.Process(target=inputQ,args=(queue,))
process.start()
record1.append(process) # output processes
for i in range(10):
process = multiprocessing.Process(target=outputQ,args=(queue,lock))
process.start()
record2.append(process) for p in record1:
p.join() queue.close() # No more object will come, close the queue for p in record2:
p.join()
8572 get: 6300(put):1555486924.3676226
8136 get: 3464(put):1555486924.412625
9576 get: 9660(put):1555486924.5126307
6936 get: 5064(put):1555486924.5976355
10652 get: 8688(put):1555486924.5976355
6992 get: 10988(put):1555486924.7526445
6548 get: 6836(put):1555486924.7456443
3504 get: 7284(put):1555486924.7666454
8652 get: 4960(put):1555486924.8536503
10868 get: 460(put):1555486924.8606508

一些进程使用put()在Queue中放入字符串,这个字符串中包含PID和时间。另一些进程从Queue中取出,并打印自己的PID以及get()的字符串。

进程池

进程池 (Process Pool)可以创建多个进程。这些进程就像是随时待命的士兵,准备执行任务(程序)。一个进程池中可以容纳多个待命的进程。

import multiprocessing as mul

def f(x):
return x ** 2 if __name__ == '__main__':
pool = mul.Pool(5)
rel = pool.map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(rel)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

我们创建了一个容许5个进程的进程池 (Process Pool) 。Pool运行的每个进程都执行f()函数。我们利用map()方法,将f()函数作用到表的每个元素上。这与built-in的map()函数类似,只是这里用5个进程并行处理。如果进程运行结束后,还有需要处理的元素,那么的进程会被用于重新运行f()函数。除了map()方法外,Pool还有下面的常用方法。

apply_async(func,args) 从进程池中取出一个进程执行func,args为func的参数。它将返回一个AsyncResult的对象,你可以对该对象调用get()方法以获得结果。

close() 进程池不再创建新的进程

join() wait进程池中的全部进程。必须对Pool先调用close()方法才能join。

共享内存

实例代码:

import multiprocessing

# Value/Array
def func1(a, arr):
a.value = 3.14
for i in range(len(arr)):
arr[i] = 0
a.value = 0 if __name__ == '__main__':
num = multiprocessing.Value('d', 1.0) # num=0
arr = multiprocessing.Array('i', range(10)) # arr=range(10)
p = multiprocessing.Process(target=func1, args=(num, arr))
p.start()
p.join()
print (num.value)
print (arr[:])
0.0
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

这里我们实际上只有主进程和Process对象代表的进程。我们在主进程的内存空间中创建共享的内存,也就是Value和Array两个对象。对象Value被设置成为双精度数(d), 并初始化为1.0。而Array则类似于C中的数组,有固定的类型(i, 也就是整数)。在Process进程中,我们修改了Value和Array对象。回到主程序,打印出结果,主程序也看到了两个对象的改变,说明资源确实在两个进程之间共享。

Manager

Manager是通过共享进程的方式共享数据。

Manager管理的共享数据类型有:Value、Array、dict、list、Lock、Semaphore等等,同时Manager还可以共享类的实例对象。

实例代码:

from multiprocessing import Process,Manager
def func1(shareList,shareValue,shareDict,lock):
with lock:
shareValue.value+=1
shareDict[1]='1'
shareDict[2]='2'
for i in xrange(len(shareList)):
shareList[i]+=1 if __name__ == '__main__':
manager=Manager()
list1=manager.list([1,2,3,4,5])
dict1=manager.dict()
array1=manager.Array('i',range(10))
value1=manager.Value('i',1)
lock=manager.Lock()
proc=[Process(target=func1,args=(list1,value1,dict1,lock)) for i in xrange(20)]
for p in proc:
p.start()
for p in proc:
p.join()
print list1
print dict1
print array1
print value1
[21, 22, 23, 24, 25]
{1: '1', 2: '2'}
array('i', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Value('i', 21)

Python multiprocessing使用详解的更多相关文章

  1. Python 字符串方法详解

    Python 字符串方法详解 本文最初发表于赖勇浩(恋花蝶)的博客(http://blog.csdn.net/lanphaday),如蒙转载,敬请保留全文完整,切勿去除本声明和作者信息.        ...

  2. python time模块详解

    python time模块详解 转自:http://blog.csdn.net/kiki113/article/details/4033017 python 的内嵌time模板翻译及说明  一.简介 ...

  3. Python中dict详解

    from:http://www.cnblogs.com/yangyongzhi/archive/2012/09/17/2688326.html Python中dict详解 python3.0以上,pr ...

  4. Python开发技术详解(视频+源码+文档)

    Python, 是一种面向对象.直译式计算机程序设计语言.Python语法简捷而清晰,具有丰富和强大的类库.它常被昵称为胶水语言,它能够很轻松的把用其他语言制作的各种模块(尤其是C/C++)轻松地联结 ...

  5. python/ORM操作详解

    一.python/ORM操作详解 ===================增==================== models.UserInfo.objects.create(title='alex ...

  6. 【python进阶】详解元类及其应用2

    前言 在上一篇文章[python进阶]详解元类及其应用1中,我们提到了关于元类的一些前置知识,介绍了类对象,动态创建类,使用type创建类,这一节我们将继续接着上文来讲~~~ 5.使⽤type创建带有 ...

  7. Python开发技术详解PDF

    Python开发技术详解(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1F5J9mFfHKgwhkC5KuPd0Pw 提取码:xxy3 复制这段内容后打开百度网盘手 ...

  8. python之数据类型详解

    python之数据类型详解 二.列表list  (可以存储多个值)(列表内数字不需要加引号) sort s1=[','!'] # s1.sort() # print(s1) -->['!', ' ...

  9. Python环境搭建详解(Window平台)

    前言 Python,是一种面向对象的解释型计算机程序设计语言,是纯粹的自由软件,Python语法简洁清晰,特色是强制用空白符作为语句缩进,具有丰富和强大的库,它常被称为胶水语言. Python是一种解 ...

随机推荐

  1. oracle11g安装补丁升级

    检查当前数据库CPU和PSU补丁信息 方法一: 登录数据库,检查DBA_REGISTRY_HIST视图. SYS@orcl> select *from dba_registry_history; ...

  2. 【2019个推开发者节】亿级日活APP都在用的个推SDK, 现在全部免费!

    1024程序员节来了 双11近了 各路满减.折扣.领券.秒杀.集赞 营销玩法猛于虎,一看优惠两毛五 日常拼命赶"需求" 修"Bug"的开发者们 想找个好用又不贵 ...

  3. [LeetCode]-DataBase-Customers Who Never Order

    Suppose that a website contains two tables, the Customers table and the Orders table. Write a SQL qu ...

  4. python之设计模式的装饰器9步学习

    在继承的基础上增加新功能,重载,重写区别 装饰器: 函数a说,我是装饰器啊,其他哪个函数顶着我,我就吃了谁,然后吐出来我的和你的返回结果 testng的UI自动化,@beforetest,@befor ...

  5. linux命令之查找find &grep

    区别:find找目录下的文件:find+目录路径+条件表达式,grep找文件中的行:grep+匹配正则表达式+文件名 find命令 find命令的一般形式 find命令的常用选项及实例 find与xa ...

  6. shift、unshift、 push、pop用法

    shift()定义和用法 shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值. 语法:arrayObject.shift() 返回值:数组原来的第一个元素的值. 说明:如果数组 ...

  7. 细说python类3——类的创建过程

    细说python类3——类的创建过程 https://blog.csdn.net/u010576100/article/details/50595143 2016年01月27日 18:37:24 u0 ...

  8. Service层获取HttpServletRequest request

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/liuyunshengsir/article/details/78183058HttpServletR ...

  9. redis4支持内存碎片清理功能使用

    最近看到redis4支持内存碎片清理了, 之前一直期待有这么一个功能, 因为之前遇到内存碎片的解决办法就是重启, 现在终于有了优雅的解决方案.\^o^/, 这个功能其实oranagra 在2017年1 ...

  10. AutoML文献阅读

    逐步会更新阅读过的AutoML文献(其实是NAS),以及自己的一些思考 Progressive Neural Architecture Search,2018ECCV的文章: 目的是:Speed up ...