python多进程-----multiprocessing包
multiprocessing并非是python的一个模块,而是python中多进程管理的一个包,在学习的时候可以与threading这个模块作类比,正如我们在上一篇转载的文章中所提,python的多线程并不能做到真正的并行处理,只能完成相对的并发处理,那么我们需要的就是python的多进程来完成并行处理,把所有的cpu资源都利用起来。multiprocessing的很大一部分与threading使用同一套API,只不过换到了多进程的环境。这里面要注意,对于多进程来说,win32平台和unix平台差别很大,我们最好在linux上完成实现。
使用这些共享API时,我们应该注意以下问题(目前这是我能想到的,以后遇到再扩充):
1、对join的处理
根据Unix环境高级编程中对进程控制一章的描述,当某个进程fork一个子进程后,该进程必须要调用wait等待子进程结束发送的sigchld信号,对子进程进行资源回收等相关工作,否则,子进程会成为僵死进程,被init收养。所以,在multiprocessing.Process实例化一个对象之后,该对象有必要调用join方法,因为在join方法中完成了对底层wait的处理,源码如下:
def join(self, timeout=None):
'''
Wait until child process terminates
'''
assert self._parent_pid == os.getpid(), 'can only join a child process'
assert self._popen is not None, 'can only join a started process'
res = self._popen.wait(timeout)
if res is not None:
_current_process._children.discard(self)
不过,调用该方法,要注意join的位置(threading模块有提到),是在每个子进程中阻塞还是在父进程中阻塞,如果在子进程中阻塞可能达不到并行处理的目的,所以要根据具体需求。而对于多线程来说,由于只有一个进程,所有子线程共享同一片内存,所以不是必须要进行join调用。例子如下:
#!/usr/bin/env python
__author__ = 'webber'
import os,time
import multiprocessing # worker function
def worker(sign, lock):
lock.acquire()
print sign, 'pid:',os.getpid()
lock.release()
time.sleep(1) # Main
print 'Main:',os.getpid() plist = []
lock = multiprocessing.Lock()
for j in range(5):
p = multiprocessing.Process(target=worker,args=('process',lock))
p.start()
plist.append(p)
p.join() #for process in record:
# process.join()
此外,还有一点关于GIL锁的说明,在python多进程中,同样需要全局解释器锁,因为每个子进程都有一把GIL,那么当它们向stdout输出时,可以同时争抢stdout资源,导致在每个子进程输出时会把不同子进程的输出字符混合在一起,无法阅读,影响代码的标志位判断,所以上例子中使用了Lock同步,在一个子进程输出完成之后再允许另一个子进程得到stdout的权限,这样避免了多个任务同时向终端输出。
2、对IPC的处理
multiprocessing包与threading模块的另一个差异特性体现在IPC上,python的multiprocessing包自带了对Pipe和Queue的管理,效率上更高,而threading模块需要与Queue模块或os.popen()、subprocess.Popen()等配合使用。
根据Unix环境高级编程的第15章进程间通信的描述,经典的IPC包括管道、FIFO、消息队列、信号量、以及共享存储。不过应用最多的还是管道。书中指出我们应该把管道看成是半双工的,并且只能在具有公共祖先的两个进程之间使用。
下面我们用一下Pipe()和Queue()方法:
a、关于Pipe()
对照书中给出的底层pipe接口函数,我们看到Pipe方法在Unix平台上实现源码如下:
def Pipe(duplex=True):
'''
Returns pair of connection objects at either end of a pipe
'''
if duplex:
s1, s2 = socket.socketpair()
s1.setblocking(True)
s2.setblocking(True)
c1 = _multiprocessing.Connection(os.dup(s1.fileno()))
c2 = _multiprocessing.Connection(os.dup(s2.fileno()))
s1.close()
s2.close()
else:
fd1, fd2 = os.pipe()
c1 = _multiprocessing.Connection(fd1, writable=False)
c2 = _multiprocessing.Connection(fd2, readable=False) return c1, c2
首先,Pipe可以是单向(half-duplex),也可以是双向的(duplex),默认为双向的。我们可以通过multiprocessing.Pipe(duplex=False)创建单向的管道。该方法返回一个元祖,包含两个文件描述符,如果为单向的,则为(read-only connection,write-only connection);如果为双向的,则为(read-write Connection, read-write Connection)。一个进程从Pipe一端输入对象(fd[1]),然后被Pipe另一端的进程接收(fd[0]),两个进程要有同一个父进程或者其中一个是父进程。单向管道只允许管道一端的进程输入,而双向管道则允许从两端输入。这里的双向管道类似于书中提到的“协同进程”的概念。
例如:
#!/usr/bin/env python
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') pipe = mul.Pipe(duplex=False)
#pipe = mul.Pipe() p1 = mul.Process(target=proc1,args=(pipe[0],)) # 读管道 p2 = mul.Process(target=proc2,args=(pipe[1],)) # 写管道
# 由于管道是单向的,对象pipe[0]只有读的权限(recv),而pipe[1]只有写的权限(send)。
#print pipe
p1.start()
p2.start()
p1.join()
p2.join()
b、关于Queue()
Queue与Pipe相类似,都是先进先出的结构,但Queue允许多个进程放入,多个进程从队列取出对象。这里可以与Queue模块相类比学习。Queue方法其实是Unix环境高级编程IPC中FIFO命名管道的实现方法。FIFO可用于有以下两种情况:
---shell命令使用FIFO将数据从一条管道传送到另一条时,无需创建中间临时文件。
---客户进程-服务器进程应用程序中,FIFO用作汇聚点,在客户进程和服务器进程二者之间传递数据。
以下就FIFO的第二种情况写一个python例子:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import multiprocessing
import time
import os # 客户进程,向众所周知的FIFO服务器进程发送请求
def client_proc(queue,msg):
request = 'I am client ' + str(msg) + ' pid: '+ str(os.getpid()) + ' time:' + str(time.time()) # 注意信息的格式,都统一为字符串类型
queue.put(request) def server_proc(queue,lock):
msg = queue.get()
lock.acquire()
print msg + '--------------->I am server ' + 'pid: ' + str(os.getpid())
lock.release() plist_cli = []
plist_ser = []
lock = multiprocessing.Lock()
queue = multiprocessing.Queue() # 参数为空,默认为队列可无限长 for i in range(10):
p1 = multiprocessing.Process(target=client_proc,args=(queue,i))
p2 = multiprocessing.Process(target=server_proc,args=(queue,lock))
p1.start()
p2.start()
plist_cli.append(p1)
plist_ser.append(p2) for proc in plist_cli:
proc.join() for proc in plist_ser:
proc.join() queue.close()
输出如下:
I am client 2 pid: 9867 time:1482489226.77--------------->I am server pid: 9879
I am client 0 pid: 9865 time:1482489226.77--------------->I am server pid: 9881
I am client 4 pid: 9869 time:1482489226.77--------------->I am server pid: 9884
I am client 1 pid: 9866 time:1482489226.77--------------->I am server pid: 9886
I am client 3 pid: 9868 time:1482489226.78--------------->I am server pid: 9888
I am client 7 pid: 9872 time:1482489226.78--------------->I am server pid: 9889
I am client 5 pid: 9870 time:1482489226.78--------------->I am server pid: 9892
I am client 6 pid: 9871 time:1482489226.78--------------->I am server pid: 9891
I am client 9 pid: 9878 time:1482489226.78--------------->I am server pid: 9893
I am client 8 pid: 9875 time:1482489226.78--------------->I am server pid: 9894
从输出可以看出,10个客户端进程把生产信息放入队列,10个服务端进程从队列取出信息并且打印,从打印时间和msg的子进程编号来看,10个服务端进程争夺stdout,通过Lock使它们有序输出,不至于输出信息混乱,msg编号没有从0排至9正是因为它们被分配给了不同的cpu资源,不同cpu资源在处理速度上不会完全一样,所以争夺stdout的能力也不同。
3、共享内存和Manager管理
众所周知,在处理多进程时,每个进程都有自己独立的内存空间,所以在多进程环境中我们应该尽量避免共享资源,否则要依赖与IPC。python的多进程除了上面提到的常用的依赖于管道和FIFO之外,还可以通过共享内存和Manager的方法来共享资源。这个不常用,由于共享内存涉及同步的问题,会降低程序的效率而不推荐使用。以后涉及到再扩展。
4、进程池
参考博客:http://www.cnblogs.com/kaituorensheng/p/4465768.html
当我们在编写网络服务端时,Unix网络编程一书中提到服务端需要fork子进程,用子进程来处理监听到的连接请求,建立连接套接字,并在子进程中关闭监听套接字,父进程中关闭连接套接字。那么,当连接的并发不是很大时,我们可以利用进程池的方式来处理到来的连接。multiprocessing.Pool可以提供指定数量的进程供用户调用,当有新的请求提交到pool中时,如果进程池还没有满,那么就会创建一个新的进程用来执行该请求;如果池中的进程数已经达到最大值,那么该请求将会阻塞等待,直到池中有进程结束,才会创建新的进程来处理该请求。
Pool方法默认的初始值如下:
def __init__(self, processes=None, initializer=None, initargs=(),maxtasksperchild=None)
通常,我们应该指定进程池的大小,如果不指定,默认为cpu的个数,即processes=cpu_count(),我们可以用该模块自带的方法查看本机的cpu个数,
print multiprocessing.cpu_count()。下面看个进程池的例子:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import multiprocessing
import time def func(msg):
print 'msg:',msg
time.sleep(3)
print 'end' pool = multiprocessing.Pool(processes=3)
for i in xrange(4):
msg = 'hello %d' % (i)
pool.apply_async(func,(msg,)) #非阻塞
# pool.apply(func,(msg,)) #阻塞,apply()源自内建函数,用于间接的调用函数,并且按位置把元祖或字典作为参数传入。 # pool.imap(func,[msg,]) #非阻塞, 注意与apply传的参数的区别
# pool.map(func,[msg,]) #阻塞 print 'Mark~~~~~~~~~~~~~~~'
pool.close()
pool.join() # 调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
print 'sub-process done'
注意apply_async和apply的差别,此外,进程池请求函数处理还可以用map,imap,注意传递参数的区别。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import multiprocessing
import time def func(msg):
print 'msg:',msg
time.sleep(3)
print 'end' pool = multiprocessing.Pool(50)
msg = range(50)
#pool.imap(func,msg) #非阻塞, 注意与apply传的参数的区别
pool.map(func,msg) #阻塞 print 'Mark~~~~~~~~~~~~~~~'
pool.close()
pool.join()
print 'sub-process done'
此外,如果子进程的处理函数中包含返回值,我们可以在父进程中对子进程调用get方法,将返回值取出,这里注意,要调用get方法的时候,进程池必须采用apply_async调用函数。例如:
if __name__ == "__main__":
pool = multiprocessing.Pool(processes=4)
result = []
for i in xrange(3):
msg = "hello %d" %(i)
result.append(pool.apply_async(func, (msg, )))
pool.close()
pool.join()
for res in result:
print ":::", res.get()
print "Sub-process(es) done."
最后,调用close()之后,进程池不再创建新的进程;
调用join()之后,wait进程池中的全部进程。必须对Pool先调用close()方法才能join。
参考博客:http://www.lxway.com/4488626156.htm
python多进程-----multiprocessing包的更多相关文章
- Python 多进程 multiprocessing.Pool类详解
Python 多进程 multiprocessing.Pool类详解 https://blog.csdn.net/SeeTheWorld518/article/details/49639651
- Python 多进程multiprocessing
一.python多线程其实在底层来说只是单线程,因此python多线程也称为假线程,之所以用多线程的意义是因为线程不停的切换这样比串行还是要快很多.python多线程中只要涉及到io或者sleep就会 ...
- Python多进程multiprocessing使用示例
mutilprocess简介 像线程一样管理进程,这个是mutilprocess的核心,他与threading很是相像,对多核CPU的利用率会比threading好的多. import multipr ...
- python ---多进程 Multiprocessing
和 threading 的比较 多进程 Multiprocessing 和多线程 threading 类似, 他们都是在 python 中用来并行运算的. 不过既然有了 threading, 为什么 ...
- python多进程(multiprocessing)
最近有个小课题,需要用到双进程,翻了些资料,还算圆满完成任务.记录一下~ 1.简单地双进程启动 同时的调用print1()和print2()两个打印函数,代码如下: #/usr/bin/python ...
- python多进程multiprocessing Pool相关问题
python多进程想必大部分人都用到过,可以充分利用多核CPU让代码效率更高效. 我们看看multiprocessing.pool.Pool.map的官方用法 map(func, iterable[, ...
- 操作系统OS,Python - 多进程(multiprocessing)、多线程(multithreading)
多进程(multiprocessing) 参考: https://docs.python.org/3.6/library/multiprocessing.html 1. 多进程概念 multiproc ...
- Python(多进程multiprocessing模块)
day31 http://www.cnblogs.com/yuanchenqi/articles/5745958.html 由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分 ...
- python多进程multiprocessing模块中Queue的妙用
最近的部门RPA项目中,小爬为了提升爬虫性能,使用了Python中的多进程(multiprocessing)技术,里面需要用到进程锁Lock,用到进程池Pool,同时利用map方法一次构造多个proc ...
随机推荐
- Makefile中的“-I”(大写i),“-L”(大写l),“-l”(小写l)
用gcc编译程序时,可能会用到“-I”(大写i),“-L”(大写l),“-l”(小写l)等参数, “-I”(大写i):表示包含头文件: “-L”(大写l):表示库文件目录: “-l”(小写l):表示链 ...
- [React] Use react-rewards to add microinteractions to React app to reward users for some actions
It's important that our users enjoy using our application or website. One way we can make it happen ...
- SpringMVC文件上传的配置
记述一下步骤以备查. 准备工作: 需要把Jakarta Commons FileUpload及Jakarta Commons io的包放lib里. 我这边的包是: commons-fileupload ...
- python实现大文件分割与合并
小U盘传大电影时可以免去用winrar分割文件时的压缩和解压缩过程. file.py import sys from os.path import exists fileCount = 0 def s ...
- Go语言格式化字符
https://github.com/polaris1119/The-Golang-Standard-Library-by-Example/blob/master/chapter01/01.3.md
- Odoo12 重大改变
Table of Contents 重构的功能 ORM 数据导入 库存 库存规则 MRP 多步路线 新功能 IoT Odoo12 预计 2018/10 在 Odoo experience 20 ...
- POJ 3687:Labeling Balls(优先队列+拓扑排序)
id=3687">Labeling Balls Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 10178 Acc ...
- xml中处理特殊字符和转义字符
XML 中的特殊字符 > 和 开始标记 > 例如: 5 ]] 如何获得这些HTML内容呢? XmlDocument doc = new XmlDocument(); doc.Load(&q ...
- 原生JS实现的h5小游戏-植物大战僵尸
代码地址如下:http://www.demodashi.com/demo/12755.html 项目介绍 本项目是利用原生js实现的h5小游戏-植物大战僵尸,主要结合了一下自己对于h5小游戏的理解,结 ...
- Android中关于cursor类介绍
使用过 SQLite 数据库的童鞋对 Cursor 应该不陌生,如果你是搞.net 开发你大可以把Cursor理解成 Ado.net 中的数据集合相当于dataReader.今天特地将它单独拿出来谈, ...