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包的更多相关文章

  1. Python 多进程 multiprocessing.Pool类详解

    Python 多进程 multiprocessing.Pool类详解 https://blog.csdn.net/SeeTheWorld518/article/details/49639651

  2. Python 多进程multiprocessing

    一.python多线程其实在底层来说只是单线程,因此python多线程也称为假线程,之所以用多线程的意义是因为线程不停的切换这样比串行还是要快很多.python多线程中只要涉及到io或者sleep就会 ...

  3. Python多进程multiprocessing使用示例

    mutilprocess简介 像线程一样管理进程,这个是mutilprocess的核心,他与threading很是相像,对多核CPU的利用率会比threading好的多. import multipr ...

  4. python ---多进程 Multiprocessing

    和 threading 的比较 多进程 Multiprocessing 和多线程 threading 类似, 他们都是在 python 中用来并行运算的. 不过既然有了 threading, 为什么 ...

  5. python多进程(multiprocessing)

    最近有个小课题,需要用到双进程,翻了些资料,还算圆满完成任务.记录一下~ 1.简单地双进程启动 同时的调用print1()和print2()两个打印函数,代码如下: #/usr/bin/python ...

  6. python多进程multiprocessing Pool相关问题

    python多进程想必大部分人都用到过,可以充分利用多核CPU让代码效率更高效. 我们看看multiprocessing.pool.Pool.map的官方用法 map(func, iterable[, ...

  7. 操作系统OS,Python - 多进程(multiprocessing)、多线程(multithreading)

    多进程(multiprocessing) 参考: https://docs.python.org/3.6/library/multiprocessing.html 1. 多进程概念 multiproc ...

  8. Python(多进程multiprocessing模块)

    day31 http://www.cnblogs.com/yuanchenqi/articles/5745958.html 由于GIL的存在,python中的多线程其实并不是真正的多线程,如果想要充分 ...

  9. python多进程multiprocessing模块中Queue的妙用

    最近的部门RPA项目中,小爬为了提升爬虫性能,使用了Python中的多进程(multiprocessing)技术,里面需要用到进程锁Lock,用到进程池Pool,同时利用map方法一次构造多个proc ...

随机推荐

  1. 报错kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 26s

    近期在服务器跑大量高负载程序,造成cpu soft lockup.如果确认不是软件的问题. 解决办法: #追加到配置文件中 echo 30 > /proc/sys/kernel/watchdog ...

  2. ArcGIS Viewer for Flex中引入google map作底图 (转)

    在ArcGIS Viewer for Flex开发中,经常需要用到google map作为底图,我们不能通过ArcGIS Viewer for Flex - Application Builder轻易 ...

  3. 关于container_of和list_for_each_entry 及其相关函数的分析

    Linux代码看的比较多了,经常会遇到container_of和list_for_each_entry,特别是 list_for_each_entry比较多,因为Linux经常用到链表,虽然知道这些函 ...

  4. hive bucket

    转载:https://www.cnblogs.com/end/archive/2013/01/09/2852413.html hive中table可以拆分成partition,table和partit ...

  5. Java高级特性—锁

    1).synchronized 加同步格式: synchronized( 需要一个任意的对象(锁) ){ 代码块中放操作共享数据的代码. } synchronized的缺陷 synchronized是 ...

  6. 解决https协议服务器内部无法跳转的问题

    <!-- 定义视图文件解析{视图解析器} --> <bean class="org.springframework.web.servlet.view.InternalRes ...

  7. [Unit Testing] Mock a Node module's dependencies using Proxyquire

    Sometimes when writing a unit test, you know that the module you're testing imports a module that yo ...

  8. Convert WebP to PNG using java

    WebP是谷歌的图片格式,java 类库imageio 是不支持此种格式的.眼下除了在线转换以及工具以外,第三方类库转换webp格式大致有: 1.linux:Google libwebp 既是类库也能 ...

  9. AndroidStudio怎么实现微信分享功能

    在应用中添加微信分享功能,需要在微信开放平台上传你的应用,审核通过后方可使用此功能. https://open.weixin.qq.com/网址 申请的过程比较简单,这里就不追溯了,贴一个友情链接 h ...

  10. Angular 学习笔记——ng-Resource1

    <!DOCTYPE HTML> <html ng-app="myApp"> <head> <meta http-equiv="C ...