簡體   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