Python doctest使用ProcessPoolExecutor挂起

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

此代码在常规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(打印
使其不会挂起,使用
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
    语句是问题所在时,我的意思是
    退出f
    ProcessPoolExecutor
    将执行
    shutdown
    方法,并导致导入死锁。您的回答解释了我下面的一个层。这两个层在各自的上下文中都是正确的。您解释了为什么它不工作,我解释了如何使它工作。