多进程
Python 多进程和多线程的使用方式非常类似,同样也支持 Lock、RLock、Condition、Semaphore、Event这几种同步方式
但每个进程享有独立的内存数据,因此需要用到 Manage、Queue、Pipe、Value 和 Array 实现数据共享
# 摘自: http://www.cnblogs.com/kaituorensheng/p/4445418.html
# 函数作为进程
import multiprocessing
import time
def worker(interval):
n = 5
while n > 0:
print("The time is {0}".format(time.ctime()))
time.sleep(interval)
n -= 1
if __name__ == "__main__":
p = multiprocessing.Process(target = worker, args = (3,))
p.start()
print "p.pid:", p.pid
print "p.name:", p.name
print "p.is_alive:", p.is_alive()
# 类作为进程
import multiprocessing
import time
class ClockProcess(multiprocessing.Process):
def __init__(self, interval):
multiprocessing.Process.__init__(self)
self.interval = interval
def run(self):
n = 5
while n > 0:
print("the time is {0}".format(time.ctime()))
time.sleep(self.interval)
n -= 1
if __name__ == '__main__':
p = ClockProcess(3)
p.start()
进程池 Pool
- 对于大数量的进程,可以用 Pool 自动管理
- 向 Pool 指定进程数量,当有新进程请求时,如果Pool还没满则创建,否则等待直到有进程结束
- 有阻塞型和非阻塞型两种,前者等当前进程结束后才开启下一个进程(顺序执行了),后者一次塞满(如果有)Pool
#coding: utf-8
import multiprocessing
import time
def func(msg):
print "msg:", msg
time.sleep(3)
print "end"
if __name__ == "__main__":
pool = multiprocessing.Pool(processes = 3)
for i in xrange(4):
msg = "hello %d" %(i)
# 非阻塞
pool.apply_async(func, (msg, )) #维持执行的进程总数为processes,当一个进程执行完毕后会添加新的进程进去
# 阻塞
# pool.apply(func, (msg, ))
print "Mark~ Mark~ Mark~~~~~~~~~~~~~~~~~~~~~~"
pool.close()
pool.join() #调用join之前,先调用close函数,否则会出错。执行完close后不会有新的进程加入到pool,join函数等待所有子进程结束
print "Sub-process(es) done."
进程间数据共享
1)队列 Queue
- 需要注意的是,这里的队列是 multiprocessing.Queu
多线程里的队列是 Queue - Pool里不支持使用Queue
from multiprocessing import Process, Queue
def test(queue):
queue.put("Hello World")
if __name__ == '__main__':
q = Queue(5)
p = Process(target=test, args=(q,)) #需要将q对象传递给子进程
p.start()
print q.get()
2)Array 和 Value
- Value 用于共享单个值,Array用于共享一个数组
- 两个都需要指定值类型
- Pool里不支持使用
import multiprocessing as mp
import time
def job(val, arr, p):
for _ in range(len(arr)):
val.value += p
arr[i] += p
print('proces %s, val=%s, arr=%s' % (p, val.value, arr[:]))
time.sleep(0.5) # 暂停0.5秒,让输出效果更明显
if __name__ == '__main__':
val = mp.Value('d', 3.14) # Value
arr = mp.Array('i', range(3)) # Array
print('val=%s, arr=%s' % (val.value, arr[:]))
print("start...")
p1 = mp.Process(target=job, args=(val, arr, 1))
p2 = mp.Process(target=job, args=(val, arr, 2))
p1.start()
p2.start()
p1.join()
p2.join()
print('val=%s, arr=%s' % (val.value, arr[:]))
print("done...")
Type code | C Type | Python Type |
---|---|---|
‘b’ | signed char | int |
‘B’ | unsigned char | int |
‘i’ | signed int | int |
‘I’ | unsigned int | int |
‘f’ | float | float |
‘d’ | double | float |
3)Manager 类
- 类似于服务器与客户之间的通信,主进程作为服务器,存放共享资源,其他进程作为客户端访问
- 提供 list、dict()等(multiprocessing.Manager.list()、multiprocessing.Manager.dict())
- Linux 和 Windows 有差异:Windows下 Manager 的声明只能在 main 下,并通过参数传给子进程(会消耗大量资源,速度很慢),Linux没这个问题
# 摘自:https://thief.one/2016/11/24/Multiprocessing%E5%85%B1%E4%BA%AB%E8%B5%84%E6%BA%90/
# Linux 下
from multiprocessing import Manager,Pool
lists=Manager().list() ##定义可被子进程共享的全局变量lists
def test(i):
print i
lists.append(i)
if __name__=="__main__":
pool=Pool()
for i in xrange(10000000):
'''
判断如果lists长度大于0,则不再往进程池中添加进程。
'''
if len(lists)<=0:
pool.apply_async(test,args=(i,))
else:
break
pool.close()
pool.join()
# Windows 下
from multiprocessing import Manager
def test(i,lists):
print i
lists.append(i)
if __name__=="__main__":
pool=Pool()
lists=Manager().list() #Manager类实例化代码只能写在main()函数里面
for i in xrange(10000000):
if len(lists)<=0:
'''
在创建子进程时,需要将lists对象传入,不然无法共享。
'''
pool.apply_async(test,args=(i,lists))##需要将lists对象传递给子进程,这里比较耗资源,原因可能是因为Manager类是基于通信的。
else:
break
4)管道 Pipe
- Pipe仅适用于两个进程通信,有单向和双向模式:
multiprocessing.Pipe(True) # 双向
multiprocessing.Pipe(False) #单向 - Pipe**效率远高于Queue**
- Pipe方法返回(conn1, conn2),代表管道的两个端,单向模式时,conn1用来接收,conn2用来发送
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import multiprocessing
import time
m = 5
def proc1(pipe):
for i in range(m):
print("proc1 send %s" % (i))
pipe.send(i)
time.sleep(1)
def proc2(pipe):
flag = 0
while True:
if flag >= m - 3:
pipe.close() # 关闭
flag += 1
print("proc2 rev %s" % (pipe.recv()))
time.sleep(1)
if __name__ == "__main__":
pipe = multiprocessing.Pipe()
p1 = multiprocessing.Process(target=proc1, args=(pipe[0],))
p2 = multiprocessing.Process(target=proc2, args=(pipe[1],))
print("start")
p1.start()
p2.start()
p1.join()
p2.join()
print("end")