由于Python的多线程并不能真正支持并行,实际上仍采用的是单核串行的方式,这与它的GIL(全局解释器锁)有关。因此,我们需要异步并行地执行某些程序时,可以采用多进程的方式。
多进程详细API可以参考链接:https://docs.python.org/zh-cn/3/library/multiprocessing.html,本文主要讲述其中的关键部分以及使用样例。
1、常用的进程池Pool类处理方法
1.1 apply
(func[, args[, kwds]])
使用 args 参数以及 kwds 命名参数调用 func , 它会返回结果前阻塞。这种情况下,apply_async()
更适合并行化工作。另外 func 只会在一个进程池中的一个工作进程中执行。
1.2 apply_async
(func[, args[, kwds[, callback[, error_callback]]]])
apply()
方法的一个变种,返回一个 AsyncResult
对象。
如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。
如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。
回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。
1.3 map
(func, iterable[, chunksize])
内置 map()
函数的并行版本 (但它只支持一个 iterable 参数,对于多个可迭代对象请参阅 starmap()
)。 它会保持阻塞直到获得结果。
这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。
注意对于很长的迭代对象,可能消耗很多内存。可以考虑使用 imap()
或 imap_unordered()
并且显示指定 chunksize 以提升效率。
1.4 map_async
(func, iterable[, chunksize[, callback[, error_callback]]])
map()
方法的一个变种,返回一个 AsyncResult
对象。
如果指定了 callback , 它必须是一个接受单个参数的可调用对象。当执行成功时, callback 会被用于处理执行后的返回结果,否则,调用 error_callback 。
如果指定了 error_callback , 它必须是一个接受单个参数的可调用对象。当目标函数执行失败时, 会将抛出的异常对象作为参数传递给 error_callback 执行。
回调函数应该立即执行完成,否则会阻塞负责处理结果的线程。
2、异步进程返回结果 AsyncResult 处理方法
AsyncResult 为 Pool.apply_async()
和 Pool.map_async()
返回对象所属的类。
get
([timeout])
用于获取执行结果。如果 timeout 不是 None
并且在 timeout 秒内仍然没有执行完得到结果,则抛出 multiprocessing.TimeoutError
异常。如果远程调用发生异常,这个异常会通过 get()
重新抛出。
wait
([timeout])
阻塞,直到返回结果,或者 timeout 秒后超时。
ready
()
返回执行状态,是否已经完成。
successful
()
判断调用是否已经完成并且未引发异常。 如果还未获得结果则将引发 ValueError
。
在 3.7 版更改: 如果没有执行完,会抛出 ValueError
异常而不是 AssertionError
。
3、多进程处理样例
本例特点:可以异步执行,每个进程传入多个参数,并且完整获取到进程的多个返回值
from multiprocessing import Pool
def test(arg1, arg2):
return "hello," + arg1, arg2 + ' test', [2222, 5, 356]
if __name__ == '__main__':
p = Pool(2)
results = []
rslt1 = p.apply_async(test, ('world1', 'tea',)) # 异步执行进程1,传入两个参数
results.append(rslt1)
rslt2 = p.apply_async(test, ('world2', 'cat',)) # 异步执行进程2,传入两个参数
results.append(rslt2)
for r in results:
a, b, c = r.get() # 获取异步进程执行结果,返回值有三个
print(a, b, c)
# 两个进程的结果输出分别为:
# hello,world1 tea test [2222, 5, 356]
# hello,world1 cat test [2222, 5, 356]
传多个参数推荐使用apply_async,如果采用map_async,无法通过p.map_async(test, ('world1', 'tea',))的方式传入多个参数 。