1.multiprocessing
multiprocessing模块提供了Process类来代表一个进程对象,
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('Child process will start.')
p.start()
p.join()
print('Child process end.')
执行结果如下:
Parent process 928.
Process will start.
Run child process test (929)...
Process end.
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process
实例,用start()
方法启动,join()
方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步,不加join则直接往后运行。
2.Pool
如果要启用大量的子进程,可以使用进程池来批量创建子进程
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(4)
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.')
执行结果如下:
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
有意设计的限制,并不是操作系统的限制。如果改成:p = Pool(5),就可以同时跑5个进程。
3.进程间的通信
queue和pipe的区别: pipe用来在两个进程间通信。queue用来在多个进程间实现通信。
pipe:即管道模式,调用Pipe()返回管道的两端的Connection 因此, Pipe仅仅适用于只有两个进程一读一写的单双工情况,也就是说信息是只向一个方向流动,新建一个Pipe(duplex)的时候,如果duplex为True,那么创建的管道是双向的;如果duplex为False,那么创建的管道是单向的。
import os, time, random from multiprocessing import Process, Pipe def proc_send(p, urls): for url in urls: print("Process %s send %s..." % (os.getpid(), url)) p.send(url) time.sleep(random.random() * 4) def proc_recv(p): while True: url = p.recv() print("Process %s recv %s..." % (os.getpid(), url)) if __name__ == '__main__': print("Main Process %s Running..." % (os.getpid())) pipe = Pipe() #该方法返回一个二元元组 (conn1,conn2),代表一个管道的两端 p1 = Process(target=proc_send, args=(pipe[0], ['url1', 'url2', 'url3', 'url4'])) p2 = Process(target=proc_recv, args=(pipe[1],)) print('Waiting subprocess done.') p1.start() p2.start() print('hi') p1.join() p2.terminate() print('Main process end.') 运行结果: Main Process 9788 Running... Waiting subprocess done. hi Process 14048 send url1... Process 8772 recv url1... Process 14048 send url2... Process 8772 recv url2... Process 14048 send url3... Process 8772 recv url3... Process 14048 send url4... Process 8772 recv url4... Main process end.
queue: Queue(队列)的使用主要是一边put(),一边get().但是Queue可以是多个Process 进行put操作,也可以是多个Process进行get()操作。
from multiprocessing import Queue, Process
from Queue import Empty as QueueEmpty
import random
def getter(name, queue):
print 'Son process %s' % name
while True:
try:
value = queue.get(True, 10)
# block为True,就是如果队列中无数据了。
# |—————— 若timeout默认是None,那么会一直等待下去。
# |—————— 若timeout设置了时间,那么会等待timeout秒后才会抛出Queue.Empty异常
# block 为False,如果队列中无数据,就抛出Queue.Empty异常
print "Process getter get: %f" % value
except QueueEmpty:
break
def putter(name, queue):
print "Son process %s" % name
for i in range(0, 1000):
value = random.random()
queue.put(value)
# 放入数据 put(obj[, block[, timeout]])
# 若block为True,如队列是满的:
# |—————— 若timeout是默认None,那么就会一直等下去
# |—————— 若timeout设置了等待时间,那么会等待timeout秒后,如果还是满的,那么就抛出Queue.Full.
# 若block是False,如果队列满了,直接抛出Queue.Full
print "Process putter put: %f" % value
if __name__ == '__main__':
queue = Queue()
getter_process = Process(target=getter, args=("Getter", queue))
putter_process = Process(target=putter, args=("Putter", queue))
getter_process.start()
putter_process.start()