在python开发期间,由于GIL的原因,不能直接采用并行的方式处理代码逻辑。在multiprocessing库的支持下,python程序能够启动子进程执行特定的任务,但子进程的管理也成为了问题。为了简化用户开发成本,python在concurrent.futures下内置了ProcessPoolExecutor这一数据结构,实现了简单的进程管理及任务调度。如果没有特别的需求,开发者只需要用ProcessPoolExecutor即可实现并行执行任务。因此,本文简单对ProcessPoolExecutor的实现进行分析,帮助大家更加了解python开发中进程/任务调度的一种方式。
首先来看ProcessPoolExecutor的用法,可以参考官方文档
可以看到用法非常简单,用户一侧只需要这样操作即可得到任务执行结果:
from concurrent.futures import ProcessPoolExecutor
def task(sleep_sec=10, tag='test'):
print('[%s] start sleep' % tag)
time.sleep(sleep_sec)
print('[%s] finish sleep' % tag)
return 100
def main():
process_pool = ProcessPoolExecutor(max_workers=3)
future = process_pool.submit(task, 3, tag='TEST')
ret = future.result()
print('result is %s' % str(ret))
process_pool.shutdown()
if __name__ == '__main__':
main()
然后就可以打印出以下内容:
[TEST] start sleep
[TEST] finish sleep
result is 100
其中,finish sleep在start sleep打印的3秒后才打印出来
简单的入口后面肯定存在精巧的逻辑。在ProcessExecutorPool源码中,有很清晰的注释去阐述这一数据结构的实现,有兴趣的读者可以直接翻越源码,debug源码来探索其中的逻辑
ProcessPoolExecutor的基础结构如下:
其中,Queue Management Thread(队列管理线程)是整个ProcessPoolExecutor的核心,不仅控制任务的收发,而且调度任务在不同进程中的执行,并且处理因为各种原因带来的进程池的异常。
以上面代码为例,ProcessPoolExecutorl整个执行流程,可以如下所示: