进程池创建时, 会克隆出一个Python解释器 , 在Linux上还会克隆原解释器中所有的程序状态, 在Windows中不会包含运行状态.
另外, 如果将多线程和多进程结合在一起使用, 需要在创建任何线程之前, 优先创建并加载进程池.
有两种方法创建进程池.
multiprocessing.Pool
进程池的使用方法如下:
复制 import time
import multiprocessing
def func ( msg ):
print ( " msg: " , msg )
time . sleep ( 3 )
print ( " end " )
pool = multiprocessing . Pool ( processes = 3 )
for i in range ( 4 ):
msg = " hello %d " % ( i )
pool . apply_async ( func , ( msg , ))
pool . close ()
pool . join ()
print ( " Sub-process(es) done. " ) 注意 :
使用apply_async 或apply 方法向子进程中添加任务, 使用这两种方法添加任务之后, 会立即在子进程中启动运行. 两者的区别在于前者是非阻塞的, 后者是阻塞的, 即使用apply方法添加后, 需要等待这个任务执行完毕, 主进程才能继续执行.
上述代码的执行结果为:
如果上述代码的apply_async方法改为apply方法, 执行的结果就是:
close 方法的作用是关闭进程池, 进程池不再接受新的任务 , join 方法的作用是阻塞父进程. 因此在调用阻塞方法时, 需要先调用进程池的close方法 , 然后再调用join方法进行阻塞.
terminate() 方法才是真正的关闭进程池的方法, 结束所有工作子进程, 不再处理未完成的任务.
为了拿到任务的执行结果, 可以将apply_async方法的返回结果进行收集:
```python
coding: utf-8
import time import multiprocessing
def func(msg): print("msg:", msg) time.sleep(3) print("end")
if name == "main ": pool = multiprocessing.Pool(processes=3) results = [] for i in range(4): msg = "hello %d" % (i,) results.append(pool.apply_async(func, (msg,)))
msg: hello 0 msg: hello 1 msg: hello 2 end msg: hello 3 end end end
```
将apply_async返回的对象收集起来, 如上面代码使用列表来收集
apply_async返回的对象是multiprocessing.pool.ApplyResult对象, 使用该对象的get()方法得到任务函数返回的结果, 如果进程还没有执行完, get()方法将会产生阻塞.
concurrent.futures.ProcessPoolExecutor
如同ThreadPoolExecutor的使用方法, 用submit或者map方法提交任务.
在ProcessPoolExecutor from concurrent.futures way slower than multiprocessing.Poolarrow-up-right 中有一个关于两者之间速度的问题. 在处理较多任务的时候, ProcessPoolExecutor方法会慢很多, 这主要是因为:
ProcessPoolExecutor对于每一个提交的任务, 都会创建一个Future对象, 里面会装在很多内容, 如最后运行的结果. 但multiprocessing.Pool就不会有这一步, 如果使用它的map方法进行提交, Pool对象会批量 地提交任务, 然后直接就把结果返回, 不会产生额外的开销.
因此:
在大批量任务 的应用场景下, 使用multiprocessing.Pool进行多进程是更好的选择.
当任务执行的时间较长时, 使用ProcessPoolExecutor带来的Futures更好.
如果需要在处理完毕后执行callback, 就只能使用ProcessPoolExecutor了, 因为Pool没有callback的方法, 只能手动实现.