concurrent.futures.ProcessPoolExecutor
ProcessPoolExecutor
属于 concurrent.futures.Executor 的一个子类,它会生成一个进程池,可以被异步调用。ProcessPoolExecutor 使用了 multiprocessing 模块来实现功能。可以用来规避计算密集或者耗时任务中的 GIL 问题。进程池只能接受和返回序列化的对象。进程总数不会超过 concurrent.futures.ProcessPoolExecutor(max_workers=None)
参数中的 max_workers
值。
简单的例子:
1 | import concurrent.futures |
定义一个 is_prime
(假装是计算密集型任务)函数判断是不是素数,给定一个数组 PRIMES
, main
函数将 is_prime
函数和数据扔进进程池,并打印出函数结果。简单好用。
ProcessPoolExecutor
比较适合计算密集型和耗时长的任务,如果对任务执行要求高的话推荐使用更加全面的 rq 或者 celery 。
concurrent.futures.ThreadPoolExecutor
ThreadPoolExecutor
与 ProcessPoolExecutor 一样,都是来自于自带的 concurrent.futures
包。属于 Python 自己提供的几个并发方案之一, futures
的意思是未完成的意思,应该指的是解决异步任务的几个方案。
顾名思义,与进程池不同的是 ThreadPoolExecutor
是线程池解决方案。他同样也是 Executor 的一个子类,因此进程池和线程池的调用方式也很类似,线程池中的最大线程数由 max_worker
参数指定,大部分机器的默认值是5。当一个 调用的一个 Future 对象等待另一个 Future 对象时,有可能会发生死锁。
简单的例子:
1 | import concurrent.futures |
定义一个函数 load_url
用来载入一个网站, 给定几个网站 URLS
, 提交函数 load_url
和 url 进线程池生成 future 对象作为 key ,url 作为 value ,生成字典 future_to_url ,从字典中依次取出完成的任务,并打印出任务结果。简单好用。
因为线程切换的性能相对进程来说更小,所以 ThreadPoolExecutor
会更加适合用于 IO 密集型小任务。当然,如果对异步任务要求高的话还是推荐 rq 或者 celery 。
loop = asyncio.get_event_loop; loop.run_in_executor
asyncio.get_event_loop.run_in_executor()
是 asyncio 下的一个模块,但是它的执行器并不是用协程来实现的,而是通过协程在后台唤起了一个线程池或者进程池。 会在执行器中调用一个函数,可能会是在进程池也可能是线程池。默认情况下会是线程池。run_in_executor()
接受的参数应该是 Executor
实例,函数以及函数需要的参数。与线程池和进程池不同的是,run_in_executor()
函数不需要指定 max_workers
参数,它会使用系统给的默认值。
简单的例子:
1 | import asyncio |
定义一个函数 sync_get_url
从 url 读取数据,定义一个异步函数 get_data_size()
,将 sync_get_url
放入线程池,并根据任务返回结果计算页面大小。定义一个异步函数 print_data_size
,读取并打印 get_data_size()
的结果。 定义一个 asyncio 任务队列,将 print_data_size
放入协程队列,依次将已完成的 print_data_size()
函数取出。简单好用。
协程比线程更轻量,更适合于 IO 密集任务。