concurrent.futures.ProcessPoolExecutor的健壮实现
项目描述
可重用进程池执行器
目标
本项目的目的是提供一个健壮、跨平台和跨版本的concurrent.futures.ProcessPoolExecutor类的实现。它特别具有以下特性
-
一致且健壮的spawn行为:在POSIX系统上,所有进程都是通过fork + exec启动的。这确保了与第三方库更安全的交互。相比之下,multiprocessing.Pool默认使用不带exec的fork,导致第三方运行时崩溃(例如OpenMP、macOS Accelerate...)。
-
可重用执行器:避免每次都重新启动完整执行器的策略。单例执行器实例可以在连续调用中重复使用(如果需要,可以动态调整大小),以限制启动和关闭开销。工作进程可以在配置的空闲超时后自动关闭,以释放系统资源。
-
透明云pickle集成:并行调用交互式定义的函数和lambda表达式。还可以注册自定义序列化实现来处理进程间通信。
-
脚本中不需要
if __name__ == "__main__":
:由于使用cloudpickle
调用定义在__main__
模块中的函数,因此在Windows下不需要保护调用并行函数的代码。 -
无死锁实现:在标准的
multiprocessing
和concurrent.futures
模块中,一个主要关注点是Pool/Executor
处理工作进程崩溃的能力。这个库旨在解决可能的死锁问题,并返回有意义的错误。请注意,Python 3.7+附带的对concurrent.futures.ProcessPoolExecutor
的实现与lokypool一样健壮,但lokypool也适用于旧版本的Python。
安装
推荐使用pip
安装loky
,
pip install loky
loky
也可以使用以下方式从源代码安装:
git clone https://github.com/joblib/loky
cd loky
python setup.py install
请注意,loky
有一个可选的依赖项psutil
,它允许早期检测内存泄漏。
用法
loky
的基本用法依赖于get_reusable_executor
,它内部管理一个自定义的ProcessPoolExecutor
对象,该对象根据上下文重复使用或重新启动。
import os
from time import sleep
from loky import get_reusable_executor
def say_hello(k):
pid = os.getpid()
print(f"Hello from {pid} with arg {k}")
sleep(.01)
return pid
# Create an executor with 4 worker processes, that will
# automatically shutdown after idling for 2s
executor = get_reusable_executor(max_workers=4, timeout=2)
res = executor.submit(say_hello, 1)
print("Got results:", res.result())
results = executor.map(say_hello, range(50))
n_workers = len(set(results))
print("Number of used processes:", n_workers)
assert n_workers == 4
有关更高级的用法,请参阅我们的文档
贡献工作流程
要为loky
做出贡献,首先在github上创建一个账户。完成此操作后,将lokypool存储库分叉以获得自己的存储库,然后在您希望工作的计算机上使用'git clone'克隆它。在您的克隆副本中做出更改,将它们推送到您的github账户,在多台计算机上进行测试,当您对它们满意时,向主存储库发送pull请求。
运行测试套件
要运行测试套件,您需要pytest
(版本>= 3)和psutil
模块。从项目的根目录运行测试套件:
pip install -e .
pytest .
为什么项目命名为loky
?
在开发loky
时,我们在尝试调试使用multiprocessing.Pool
和concurrent.futures.ProcessPoolExecutor
时遇到了一些调试死锁的糟糕经历,特别是在项目初期调用带有不可序列化参数或返回值的函数时。当我们需要选择一个名称时,我们已经处理了这么多死锁,我们想要一些可以驱散它们的呼唤!因此,loky
:神、锁和y
的组合,使其有点酷和亲切:(而且也不太可能在谷歌搜索结果中产生名称冲突 ^^)。
为避免在concurrent.futures
中的死锁而做出的修复也作为Python 3.7+的上游贡献,这是一种不太神秘的驱散死锁的方式 :D
致谢
这项工作得到数据科学中心的支持,该中心由IDEX Paris-Saclay资助,ANR-11-IDEX-0003-02
项目详细信息
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。