Python doctest使用ProcessPoolExecutor

时间:2018-01-12 02:55:19

标签: 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 -m doctest myfile.py运行,它会挂起。将submit(job更改为submit(print会使其不会挂起,就像使用ThreadPoolExecutor而不是ProcessPoolExecutor一样。

为什么在doctest下运行时会挂起?

4 个答案:

答案 0 :(得分:3)

问题是导入模块获取了一个锁(哪个锁取决于你的python版本),请参阅docs for imp.lock_held

锁是在多处理上共享的,所以你的死锁是因为主进程在导入模块时加载并等待尝试导入模块的子进程,但无法获取锁导入它,因为它是目前正由您的主要流程导入。

以步骤形式:

  1. 主进程获取锁以导入myfile.py
  2. 主进程开始导入myfile.py (它必须导入myfile.py,因为这是定义job()函数的地方,这就是print()函数没有死锁的原因myfile.py 1}})
  3. 主进程启动并阻止子进程。
  4. 子流程尝试获取锁以导入N
  5. =>死锁。

答案 1 :(得分:2)

所以我认为这个问题是因为你的with陈述。如果你有以下

with concurrent.futures.ProcessPoolExecutor(1) as pool:
    pool.submit(job, "hello")

它强制执行线程然后关闭它自己。当您将其作为主进程运行时,它可以工作并为线程提供执行作业的时间。但是当你import它作为一个模块时,它不会给后台线程一个机会,并且池上的shutdown等待执行工作,因此deadlock

因此,您可以使用的解决方法是

import concurrent.futures

def job(text):
    print(text)

pool = concurrent.futures.ProcessPoolExecutor(1)
pool.submit(job, "hello")

if __name__ == "__main__":
    pool.shutdown(True)

如果您愿意,这会阻止deadlock并允许您运行doctest以及import模块

答案 2 :(得分:0)

doctest导入您的模块以进行处理。尝试添加此项以防止导入时执行:

if __name__ == "__main__":
    with concurrent.futures.ProcessPoolExecutor(1) as pool: 
        pool.submit(job, "hello")

答案 3 :(得分:0)

这实际上应该是一个评论,但它太长,不能成为一个。

如果代码也作为模块导入,则代码会失败,并且与doctest的错误相同。我得到_pickle.PicklingError: Can't pickle <function job at 0x7f28cb0d2378>: import of module 'a' failed(我将文件命名为a.py)。

您缺少if __name__ == "__main__":违反了多处理的编程指南: https://docs.python.org/3.6/library/multiprocessing.html#the-spawn-and-forkserver-start-methods

我猜孩子进程也会尝试导入模块,然后尝试启动另一个子进程(因为池无条件地执行)。但我对此并不是百分之百确定。 我也不确定为什么你得到的错误是can't pickle <function>

这里的问题似乎是您希望模块在导入时自动启动进程。我不确定这是否可行。