ProcessPoolExecutor 的递归使用被挂起

问题描述 投票:0回答:1

问题

我尝试使用

ProcessPoolExecutor
进行递归调用,但它不起作用。我在下面创建了一个最小的示例

from concurrent.futures import ProcessPoolExecutor
from time import sleep

executor = ProcessPoolExecutor()
i = 3

def test():
    global i
    print("top")
    if i == 0:
        print("bar")
        return
    else:
        print("foo")
        i -= 1
        p = executor.submit(test)
        print(p.result())
        print(i)

test()

我期望发生的是

i
递减,在进程池中进行递归调用,然后我们等待结果。这种情况会递归发生,直到
i
达到 0,此时调用会级联回来。然而,这并没有发生。我看到以下输出

top
foo
top
foo

在哪一点挂起

可能出现的问题

比赛条件

我认为这可能是一个竞争条件,并且

i
并不是每次都会递减,但这应该不重要,因为该函数应该被调用多少次

全局状态

我通过使递归调用以某种随机概率发生来消除对全局状态的依赖。两次调用后仍然挂起。

编辑:我使用的是 Ubuntu 23

python concurrency concurrent.futures
1个回答
0
投票

您对这里事物运作方式的心理模型在很多方面与现实不匹配。这东西一开始很微妙!

不过,我不会写一本小书。让我们从一些基础知识开始。首先,按照文档的建议使用

if __name__ == "__main__":
防护装置。跳过它,你就跳入了深渊。当您是专家时,您可以跳过救生圈,但在此之前您很可能会沉没;-)

其次,什么都没有在进程之间“神奇地”共享。由于您在 Linux-y 系统上运行,因此在幕后调用

ProcessPoolExecutor()
会调用
fork()
,并且它创建的工作进程将获得调用进程状态的 copy 截至时间
ProcessPoolExecutor()
是叫。在一个进程中对变量绑定所做的更改不会对其他进程看到的绑定产生任何影响。
global
(或缺乏)与此无关。

因此,首先研究这个简单得多的代码,直到理解这些要点:

i = 3

def test():
    print(f"i in test {i}")
    return

if __name__ == "__main__":
    from concurrent.futures import ProcessPoolExecutor
    executor = ProcessPoolExecutor() # all workers will see i==3
    i -= 1 # has no effect on workers
    print(f"i in main {i}")
    p = executor.submit(test)
    print(f"result {p.result()}")

显示

i in main 2
i in test 3
result None

原因已经解释过了。

© www.soinside.com 2019 - 2024. All rights reserved.