Python doctest使用ProcessPoolExecutor挂起
此代码在常规CPython 3.5下运行良好:Python doctest使用ProcessPoolExecutor挂起,python,python-multiprocessing,doctest,concurrent.futures,process-pool,Python,Python Multiprocessing,Doctest,Concurrent.futures,Process Pool,此代码在常规CPython 3.5下运行良好: import concurrent.futures def job(text): print(text) with concurrent.futures.ProcessPoolExecutor(1) as pool: pool.submit(job, "hello") 但是如果您以python-mdoctestmyfile.py运行它,它将挂起。将submit(作业更改为submit(打印使其不会挂起,使用ThreadPool
import concurrent.futures
def job(text):
print(text)
with concurrent.futures.ProcessPoolExecutor(1) as pool:
pool.submit(job, "hello")
但是如果您以python-mdoctestmyfile.py运行它,它将挂起。将submit(作业
更改为submit(打印
使其不会挂起,使用ThreadPoolExecutor
而不是ProcessPoolExecutor
时也不会挂起
为什么在doctest下运行时它会挂起?doctest会导入您的模块以对其进行处理。请尝试添加此项以防止在导入时执行:
if __name__ == "__main__":
with concurrent.futures.ProcessPoolExecutor(1) as pool:
pool.submit(job, "hello")
这实际上应该是一个评论,但它太长了 如果代码也作为模块导入,则会失败,错误与doctest相同。我得到
\u pickle.PicklingError:无法pickle:导入模块“a”失败
(我将文件命名为a.py
)
如果缺少,则会违反多处理的编程准则:
我猜子进程也会尝试导入模块,然后模块会尝试启动另一个子进程(因为池是无条件执行的)。
我也不确定为什么您会遇到无法pickle
的错误
这里的问题似乎是您希望模块在导入时自动启动一个进程。我不确定这是否可行。因此我认为问题是因为您的with
语句。当您
with concurrent.futures.ProcessPoolExecutor(1) as pool:
pool.submit(job, "hello")
它强制执行要执行和关闭的线程,然后关闭线程本身。当您将其作为主进程运行时,它会工作并为线程提供执行作业的时间。但是当您将其作为模块导入时,它不会给后台线程一个机会,池上的关闭会等待工作执行,因此a死锁
因此,您可以使用的解决方法如下所示
import concurrent.futures
def job(text):
print(text)
pool = concurrent.futures.ProcessPoolExecutor(1)
pool.submit(job, "hello")
if __name__ == "__main__":
pool.shutdown(True)
这将防止死锁,并允许您运行doctest
以及import
模块(如果需要)问题是导入模块会获得锁(该锁取决于您的python版本),请参阅
锁在多进程上共享,因此会发生死锁,因为主进程在导入模块时加载并等待一个子进程,该子进程尝试导入模块,但无法获取锁以导入模块,因为它当前正由主进程导入
以阶梯形式:
主进程获取锁以导入myfile.py
主进程开始导入myfile.py
(它必须导入myfile.py
,因为这是定义job()
函数的地方,这就是它没有为print()
死锁的原因)
主进程启动并阻塞子进程
子流程尝试获取锁以导入myfile.py
=>死锁。通过阻止代码一起运行来避免问题。但我不想阻止代码运行,我想阻止它挂起。代码应该在加载模块时运行(例如,通过doctest或常规导入),或作为独立脚本运行。我明白你的意思。不过,问题是我希望能够在doctest中启动ProcessPoolExecutor。这是我无法实现的。只需将所有代码隐藏在if name==“main”
下就不起作用,因为这会阻止代码运行(在doctest下)。为什么不将ProcessPoolExecutor的代码放在doctest字符串中,以便它作为测试运行?或者是否有其他用例?我发布的答案是否有任何更新/反馈?这个答案有点误导,因为问题不在于with
语句。您可以通过正在执行pool=…ProcessPoolExecutor()
pool.submit(…)
pool.shutdown()
。正如我在回答中指出的,问题在于导入锁。@Daphtdaz,我同意你的看法。我不知道https://docs.python.org/3/library/imp.html#imp.lock_held
引用我的回答,我只知道这是一个导入死锁。当我说with
语句是问题所在时,我的意思是退出fProcessPoolExecutor
将执行shutdown
方法,并导致导入死锁。您的回答解释了我下面的一个层。这两个层在各自的上下文中都是正确的。您解释了为什么它不工作,我解释了如何使它工作。