Python 多处理管理器在烧瓶 API 中使用时显示错误

Posted

技术标签:

【中文标题】Python 多处理管理器在烧瓶 API 中使用时显示错误【英文标题】:Python multiprocessing manager showing error when used in flask API 【发布时间】:2021-12-05 12:59:35 【问题描述】:

我对做我想做的事情的最佳方式感到很困惑。

我想要什么?

    对烧瓶应用程序的 API 调用 Flask 路由使用 Process 模块启动 4-5 个多进程,并使用共享的 Managers().list() 组合结果(在切片的 pandas 数据帧上) 将计算结果返回给客户端。

我的实现:

pos_iter_list = get_chunking_iter_list(len(position_records), 10000)

manager = Manager()
data_dict = manager.list()
processes = []
for i in range(len(pos_iter_list) - 1):
    temp_list = data_dict[pos_iter_list[i]:pos_iter_list[i + 1]]
    p = Process(
        target=transpose_dataset,
        args=(temp_list, name_space, align_namespace, measure_master_id, df_searchable, products,
              channels, all_cols, potential_col, adoption_col, final_segment, col_map, product_segments,
              data_dict)
    )
    p.start()
    processes.append(p)
for p in processes:
    p.join()

我的目录结构:

- main.py(flask entry point)
- helper.py(contains function where above code is executed & calls transpose_dataset function)

我在运行时遇到的错误? RuntimeError:找不到提供的模块“mp_main”的根路径。这可能是因为模块来自不提供文件名信息的导入钩子,或者因为它是一个命名空间包。在这种情况下,需要显式提供根路径。

不确定这里出了什么问题,当使用 if __name__ == '__main__': 从 sample.py 文件调用管理器列表时,管理器列表工作正常

更新:同一段代码在我的 MacBook 上运行良好,但在 windows 操作系统上却没有。

烧瓶 API 调用示例:

@app.route(PREFIX + "ping", methods=['GET'])
def ping():
    man = mp.Manager()
    data = man.list()
    processes = []
    for i in range(0,5):
        pr = mp.Process(target=test_func, args=(data, i))
        pr.start()
        processes.append(pr)

    for pr in processes:
        pr.join()

    return json.dumps(list(data))

【问题讨论】:

完整的回溯会很有帮助。当使用“spawn”与“fork”时,这几乎可以肯定是关于可导入性的代码布局/结构问题(我假设你的 macos 有一个稍微旧的 python 版本仍然默认为“fork”) 你提到你正在使用if __name__ == "__main__":,但你必须确保基本上除了函数和类定义之外的所有内容都在其中。听起来有点像烧瓶试图在子进程中启动一个新的服务器实例,但失败了。 如果您使用 docs quickstart 中的样板文件,可能归结为 app = Flask(__name__).. 将它和您所有的 @app.route 函数定义放在 if __name__ == "__main__": 块中,以防止它尝试在子节点上构建另一个服务器流程导入。 可能可以更改为app = Flask("__main__"),但不知道这意味着什么。 @Aaron 这对我有用但是,理解为什么子进程尝试再次启动烧瓶应用程序有点令人困惑? MacOS 不会发生同样的情况。 【参考方案1】:

Stack 有一个持续存在的错误,阻止我发表评论,所以我会写一个答案..

Python 有两种(主要)方式来启动新进程:“spawn”和“fork”。 fork 是仅在 *nix 中可用的系统命令(阅读:linux 或 macos),因此 spawn 是 windows 中的唯一选项。 3.8 之后 spawn 将是 MacOS 上的默认设置,但 fork 仍然可用。最大的不同是 fork 基本上复制了现有进程,而 spawn 启动了一个全新的进程(就像只是打开一个新的 cmd 窗口)。为什么和如何有很多细微差别,但是为了能够运行您希望子进程使用 spawn 运行的功能,子进程必须 import 主文件。导入文件就等于只执行该文件,然后通常将其命名空间绑定到变量:import flask 将运行 flask/__ini__.py 文件,并将其全局命名空间绑定到变量 flask。然而,通常有代码仅由主进程使用,不需要在子进程中导入/执行。在某些情况下,再次运行该代码实际上会破坏事情,因此您需要阻止它在主进程之外运行。考虑到这一点,“魔术”变量__name__ 仅等于主文件中的"__main__"(而不是在子进程中或在导入模块时)。

在您的特定情况下,您正在创建一个新的app = Flask(__name__),它会在您运行服务器之前进行一些验证和检查。这是从子进程运行时遇到的这些设置/验证步骤之一。通过根本不让它运行来修复它是更清洁的解决方案,但您也可以通过给它一个不会绊倒的值来修复它,然后永远不要启动该辅助服务器(再次通过使用 if __name__ == "__main__": 保护它)

【讨论】:

以上是关于Python 多处理管理器在烧瓶 API 中使用时显示错误的主要内容,如果未能解决你的问题,请参考以下文章

Python、Tkinter 和烧瓶运行时 0xC0000005

如何在 Python 的单独线程中启动烧瓶 api 服务器

烧瓶用户认证

有烧瓶的背景工作者

从 Python 烧瓶 API 返回空白

使用 bcrypt 在烧瓶管理员中更改密码