首页 > 编程问答 >ProcessPoolExecutor 的递归使用挂起

ProcessPoolExecutor 的递归使用挂起

时间:2024-07-25 10:29:53浏览次数:10  
标签:python concurrency concurrent.futures

问题

我尝试将 a 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


在理解 ProcessPoolExecutor 在 Python 中的工作方式时遇到了常见问题。问题不在于竞争条件或直接与全局状态相关,而在于 进程彼此隔离

当使用 ProcessPoolExecutor 时,是在创建进程池。每个进程都有自己独立的内存空间。这意味着在一个进程中对全局变量 i 所做的任何更改在其他进程中都不可见。

让我们分解一下代码中发生的事情:

  1. 初始调用: test() 调用开始, i 初始化为 3。
  2. 第一次递归: test() 被调用,打印 "top" 和 "foo", i 当前进程的内存中 递减为 2,并使用 executor.submit(test) 提交一个新任务给进程池。
  3. 第二个进程: 进程池启动一个新进程来处理 executor.submit(test) 这个新进程有它自己的 i 副本,它从 3 开始,而不是 2 。它打印 "top" 和 "foo",并且它版本的 i 递减为 2。然后它提交另一个任务给进程池。
  4. 挂起: 此时,有两个进程都在等待 p.result() 。但是,这两个进程提交的任务都依赖于 i 达到 0。由于每个进程都有自己的 i 副本,并且它们都在递减自己的本地副本,因此该条件永远不会满足,导致挂起。

解决方案:

为了解决这个问题,需要一种在进程之间共享 i 的值的方法。可以通过多种方式实现此目的:

  • 共享内存( multiprocessing.Value multiprocessing.Array ): 这些对象允许在进程之间共享内存中的值或数组。
  • 队列( multiprocessing.Queue ): 队列提供了一种在进程之间安全地传递消息(因此是数据)的方式。可以使用队列在工作进程和主进程之间传递 i 的更新值。
  • 其他 IPC 机制: Python 的 multiprocessing 模块提供了其他进程间通信 (IPC) 机制,例如管道和管理器,这些机制可用于更复杂的数据共享场景。

示例(使用 multiprocessing.Value ):

from concurrent.futures import ProcessPoolExecutor
from multiprocessing import Value
from time import sleep

i = Value('i', 3)  # 使用共享内存创建 'i'

def test():
    global i
    print("top")
    if i.value == 0:
        print("bar")
        return
    else:
        print("foo")
        with i.get_lock():  # 保护对共享变量的访问
            i.value -= 1
        p = executor.submit(test)
        print(p.result())
        print(i.value)

if __name__ == '__main__':  # 保护入口点
    with ProcessPoolExecutor() as executor:
        test()

在这个版本中,我们使用 multiprocessing.Value 创建了一个共享的 i with i.get_lock(): 块确保对 i 的递减操作是原子的,防止了竞争条件。

请记住,多处理引入了额外的复杂性。在处理进程池和共享数据时,必须谨慎管理同步和通信以避免错误并确保正确性。

标签:python,concurrency,concurrent.futures
From: 78790478

相关文章

  • 在 VSCode 中激活 conda env 时,“which python”和“sys.path”给出不同的输出
    当我激活condaenv时,选择相应的解释器并在VScode中启动Python乍一看一切似乎都很好:(My_env)name@my_computer:~/Bureau/My_env/Code/current_dir$/home/name/anaconda3/envs/My_env/bin/python-mIPython--no-autoindentPython3.12.4|packagedb......
  • Python课程设计项目期末大作业(含免费源代码)
    选题代码下载地址:https://download.csdn.net/download/s44359487yad/89572689选题程序名称:口红色号识别器选题:基于图像处理、人脸识别和Flask构建的图片口红色号识别及商品推荐系统功能:根据上传的图片自动化图像处理后分析脸部特征,并判断其嘴部妆容状态,依托现......
  • python webbrowser.open 不使用默认浏览器
    对你们来说这是一个好奇的家伙..在我的python程序中webbrowser.open('etc..')打开MicrosoftEdge现在奇怪的是,我在与opensChrome(我的默认值)稍有不同的文件夹中还有另一个python程序关于发生了什么的任何想法吗?!!(我知道有人问过类似的问题,但......
  • 如何在 Mac 上运行 Python 文件来读取 txt 文件并将其写入外部硬盘?
    我目前有一个充满了我想阅读的epub的文件夹,一个我已经阅读过并想再次阅读的epub的文件夹,以及一个相应的文件,其中每个文件都有epub文件的名称。问题是,这些文件夹仅位于我的外部硬盘上。我想要做的是让我的脚本解析这些文件夹中的epub列表,并在我的下载文件夹中创建最新的副......
  • 深入探索:使用Python进行网站数据加载逻辑分析与请求
    作为一名资深的Python程序员,我经常需要从网站中提取数据以供分析或进一步处理。这项任务涉及到对网站数据加载逻辑的深入分析,以及使用Python进行高效的网络请求。在本文中,我将分享如何分析网站的数据加载方式,并使用Python的requests库来模拟浏览器行为,获取所需的数据。网站......
  • 如何将 Python 列表添加到 Excel 中已有值的列的末尾?
    我目前正在尝试编写一个程序,将值附加到列表中,然后将这些值添加到Excel数据表中的列中。每次运行该程序时,我都希望在同一列的末尾添加更多值。所以我不确定如何解决这个问题,而且我在网上找到的其他答案也没有取得多大成功。以下是使用openpyxl库在Python中将......
  • 如何学习Python:糙快猛的大数据之路(学习地图)
    在这个AI和大数据主宰的时代,Python无疑是最炙手可热的编程语言之一。无论你是想转行还是提升技能,学习Python都是一个明智之选。但是,该如何开始呢?今天,让我们聊聊"糙快猛"的Python学习之道。什么是"糙快猛"学习法?"糙快猛"学习法,顾名思义,就是:糙:不追求完美,允许存......
  • Python 中 __get__ 方法的内部原理
    我正在摆弄描述符,结果碰壁了。我以为我可以像使用任何其他方法一样直接调用它,但显然,它似乎不一致或者我遗漏了一些东西。假设我有一个用作描述符的坐标类:|||还有一个Point类,它有2个坐标属性:classCoordinate:def__set_name__(self,owner,name):self._na......
  • 使用带有私钥的云前端生成签名 URL 的问题..使用 Python 3.7 为带有空格的 S3 对象生
    我在使用Python3.7为S3对象生成签名URL时遇到问题。具体来说,键中带有空格的对象的URL会导致“访问被拒绝”错误,而没有空格的对象的URL通常工作正常。但是,并非所有不带空格的对象都能正常工作,带空格的对象始终会失败。fromdatetimeimportdatetime,timedeltaimpo......
  • 有没有更好的方法来在存储库中的一组 python 程序之间共享公共代码
    当我想要快速、轻松地做许多不同的事情时,我会选择Python-即我总是会得到许多Python“程序”-例如一组脚本-或者如果我正在玩一些东西,一堆测试程序等-即始终是许多不同程序的松散集合。但是,我会分享某些内容。例如,如果我正在使用AI-我可能有30个左右完全不相......