文章目录
- 0. 前言
- 1. ProcessPoolExecutor
- 2. multiprocessing
0. 前言
- Python中的多线程与多进程:
- Python中由于全局解释器(Global Interpreter Lock, GIL)的关系,多线程程序默认只能使用CPU的一个核。
- Python多线程适用于IO密集型任务。
- Python多进程则能够跳过GIL的限制,使用多个CPU核,适用于计算密集型任务。
- Python中的多进程主要通过
ProcessPoolExecutor
或 multiprocessing
实现。
- 需要注意的是,
ProcessPoolExecutor
是通过 multiprocessing
包实现的。
- 参考资料:
- 官方文档:ProcessPoolExecutor
- 官方文档:multiprocessing
1. ProcessPoolExecutor
ProcessPoolExecutor
的是 concurrent.futures.Executor
的子类。
- 构造器:
ProcessPoolExecutor(max_workers=None, mp_context=None, initializer=None, initargs=())
max_workers
:设置最大进程数量。
initializer
& initargs
:在创建每个进程时会调用的函数,以及函数对应参数。
mp_context
:多进程上下文。
- 主要方法:
submit(fn, *args, **kwargs)
:提交任务,返回Future
对象。
shutdown(wait=True)
:关闭线程池,如果wait=True
则等待所有启程执行完毕后结束才,否则就是马上返回(但正在执行的会继续执行完毕)。
map(func, *iterables, timeout=None, chunksize=1)
- 举例
import concurrent.futures
import math
PRIMES = [
112272535095293,
112582705942171,
112272535095293,
115280095190773,
115797848077099,
1099726899285419]
def is_prime(n):
if n % 2 == 0:
return False
sqrt_n = int(math.floor(math.sqrt(n)))
for i in range(3, sqrt_n + 1, 2):
if n % i == 0:
return False
return True
def main():
with concurrent.futures.ProcessPoolExecutor() as executor:
for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
print('%d is prime: %s' % (number, prime))
if __name__ == '__main__':
main()
2. multiprocessing
2.1. Process
- 类似于
threading.Thread
,用于创建单个进程。
- 与
threading.Thread
相同的API:
start()
:启动进程
run()
:启动进城后执行的函数
join()
:当A进程调用B进程的join()
方法后,A进程会等待B进程执行完毕后再继续执行下一步。
name
:进程名称。
daemon
:是否为守护进程,如果需要改变数值,则需要在执行 start
前完成。
is_alive()
:判断是否正在执行。
- 独有的API:
pid
terminate()
:终止进程,不会执行退出处理程序和finally子句等
kill()
:与 terminate()
相同。
close()
:关闭Process对象,释放对应资源。
2.2. Pool
- 进程池的另一种实现,全名为
multiprocessing.pool.Pool
- 初始化函数:
Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])
processes
:最大进程数量,默认为 os.cpu_count()
initializer & initargs
:启动进程时会调用的函数以及对应的参数。
context
:进程池上下文。
maxtasksperchild
:退出或被一个新的工作进程代替之前能完成的任务数量。
- 默认为None,即生命周期与进程池相同。
- 主要作用是释放资源。
- 主要方法:
apply(func[, args[, kwds]])
:使用进程池中某一个进程调用该方法。会阻塞直到返回结果。
apply_async(func[, args[, kwds[, callback[, error_callback]]]])
:
apply
的变种,不会阻塞。
- 如果正常执行则会调用
callback
对象,该对象接受单个参数(猜测就是函数输出)。
- 如果出错了,则会调用
error_callback
对象,该对象接受单个参数,该参数是抛出的异常对象。
map(func, iterable[, chunksize])/map_async
:内置函数map()
的并行版本。
- 这个方法会将可迭代对象分割为许多块,然后提交给进程池。可以将 chunksize 设置为一个正整数从而(近似)指定每个块的大小可以。
- 注意对于很长的迭代对象,可能消耗很多内存。可以考虑使用 imap() 或 imap_unordered() 并且显示指定 chunksize 以提升效率。
starmap(func, iterable[, chunksize])/starmap_async
:与map
类似,猜测就是等价于对iterable
对象再包裹上一层func
imap(func, iterable[, chunksize])
:map
的延迟执行版本。
imap_unordered(func, iterable[, chunksize])
- 与
imap
相同,区别在于通过迭代器返回的结果是任意的。
close()
:阻止后续任务提交到进程池,当所有任务执行完成后,工作进程会退出。
terminate()
:不必等待未完成的任务,立即停止工作进程。当进程池对象呗垃圾回收时, 会立即调用 terminate() 。
join()
:等待工作进程结束。
- 调用
join()
前必须先调用 close()
或者 terminate()
- 举例:
from multiprocessing import Pool
import time
def f(x):
return x*x
if __name__ == '__main__':
with Pool(processes=4) as pool:
result = pool.apply_async(f, (10,))
print(result.get(timeout=1))
print(pool.map(f, range(10)))
it = pool.imap(f, range(10))
print(next(it))
print(next(it))
print(it.next(timeout=1))
result = pool.apply_async(time.sleep, (10,))
print(result.get(timeout=1))