繁体   English   中英

Python `multiprocessing` 使用基础 Python 生成进程,而不是 virtualenv Python

[英]Python `multiprocessing` spawned process using base Python, not virtualenv Python

在 python 的标准安装中(例如通过 miniconda),我运行这个脚本(也粘贴在下面)并得到以下 output:

python test_python_multiprocessing.py
arg1: called directly
sys.executable: C:\ProgramData\Miniconda3\envs\python3_7_4\python.exe
-----

arg1: called via multiprocessing
sys.executable: C:\ProgramData\Miniconda3\envs\python3_7_4\python.exe
-----

The two exes:
C:\ProgramData\Miniconda3\envs\python3_7_4\python.exe
C:\ProgramData\Miniconda3\envs\python3_7_4\python.exe

这是我所期望的。

当我从一个虚拟环境中的 python 运行相同的脚本时,该虚拟环境的基础是嵌入脚本应用程序中的 python,我得到以下结果:

arg1: called directly
sys.executable: C:\[virtual_environment_path]\Scripts\python.exe
-----

arg1: called via multiprocessing
sys.executable: C:\[application_with_python]\contrib\Python37\python.exe
-----

The two exes:
C:\[virtual_environment_path]\Scripts\python.exe
C:\[application_with_python]\contrib\Python37\python.exe
Traceback (most recent call last):
  File ".\test_python_multiprocessing.py", line 67, in <module>
    test_exes()
  File ".\test_python_multiprocessing.py", line 64, in test_exes
    assert exe1 == exe2
AssertionError

至关重要的是,子sys.executable与父sys.executable不匹配,而是与父的基匹配。

我怀疑应用程序附带的 python 已被更改,可能是为了让生成的进程指向硬编码的 python 路径。

  1. 我查看了应用程序随附的 python 标准库,但没有发现任何可以解释这种行为差异的差异。

  2. 我尝试手动将可执行文件设置为 multiprocessing.Process 之前的默认值multiprocessing.Process multiprocessing.set_executable(sys.executable)multiprocessing.get_context("spawn").set_executable(sys.executable) 这些没有影响。

标准 python 安装与嵌入脚本应用程序中的 python 之间的行为差异有哪些可能的解释? 我如何调查原因,并在生成子进程时强制使用正确的 python 可执行文件?

test_python_multiprocessing.py

import multiprocessing

def functionality(arg1):
    import sys
    print("arg1: " + str(arg1))
    print("sys.executable: " + str(sys.executable))
    print("-----\n")
    return sys.executable

def worker(queue, arg1):
    import traceback
    try:
        retval = functionality(arg1)
        queue.put({"retval": retval})
    except Exception as e:
        queue.put({"exception": e, "traceback_str": traceback.format_exc()})
        raise
    finally:
        pass

def spawn_worker(arg1):
    queue = multiprocessing.Queue()
    p = multiprocessing.Process(target=worker, args=(queue, arg1,))
    p.start()
    p.join()
    
    err_or_ret = queue.get()

    handle_worker_err(err_or_ret)
    if p.exitcode != 0:
        raise RuntimeError("Subprocess failed with code " + str(p.exitcode) + ", but no exception was thrown.")
    return err_or_ret["retval"]

def handle_worker_err(err_or_ret):
    if "retval" in err_or_ret:
        return None
    err = err_or_ret
    #import traceback
    if (err is not None):
        #traceback.print_tb(err["traceback"]) # TODO use e.g. tblib to get traceback
        print("The exception was thrown in the child process, reraised in parent process:")
        print(err["traceback_str"])
        raise err["exception"]

def test_exes():
    exe1 = functionality("called directly")
    exe2 = spawn_worker("called via multiprocessing")

    print("The two exes:")
    print(exe1)
    print(exe2)

    assert exe1 == exe2

if __name__ == "__main__":
    test_exes()

[编辑] 我在可编写脚本的应用程序中嵌入的 python 上检测到这个问题是一个转移注意力的问题。 使用“标准安装”Python 3.7.4 基础制作虚拟环境也有同样的问题。

长话短说,使用“虚拟”解释器会导致多处理中出现错误,开发人员决定将 virtualenv 环境重定向到基础环境。 问题 35797 的链接

这是从popen_spawn_win32.py中提取的

# bpo-35797: When running in a venv, we bypass the redirect
# executor and launch our base Python.

一种解决方案是改用subprocess ,并通过套接字连接到您的“管道”而不是使用多处理,您可以在BaseManager 文档中看到如何使用套接字连接到管理器,python 使它像插入一样简单它的端口号。

你也可以尝试pathos因为它的多处理实现是“不同的”,(我认为它的池使用 sockets,但我之前没有深入研究它并且它以不同的方式产生新工人的方式还有其他问题,但它可以在一些多处理失败的奇怪环境。)

编辑:另一个实际使用 sockets 的不错的并行化替代方案是Dask ,但您必须单独启动工作人员,而不是通过其内置池。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM