由于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(funciterable[, chunksize])

内置 map() 函数的并行版本 (但它只支持一个 iterable 参数,对于多个可迭代对象请参阅 starmap())。 它会保持阻塞直到获得结果。

这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。

注意对于很长的迭代对象,可能消耗很多内存。可以考虑使用 imap() 或 imap_unordered() 并且显示指定 chunksize 以提升效率。

1.4 map_async(funciterable[, 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',))的方式传入多个参数 。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐