You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python嵌入式冻结应用中如何成功启动子进程?

Windows嵌入式冻结Python应用中multiprocessing子进程启动失败

我了解到Python的multiprocessing模块中子进程的入口是multiprocessing.spawn.spawn_main,根据multiprocessing.spawn.get_command_line的实现:

def get_command_line(**kwds):
    '''
    Returns prefix of command line used for spawning a child process
    '''
    if getattr(sys, 'frozen', False):
        return ([sys.executable, '--multiprocessing-fork'] +
                ['%s=%r' % item for item in kwds.items()])
    else:
        prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)'
        prog %= ', '.join('%s=%r' % item for item in kwds.items())
        opts = util._args_from_interpreter_flags()
        exe = get_executable()
        return [exe] + opts + ['-c', prog, '--multiprocessing-fork']

在常规Python环境中,Windows上启动子进程的命令如下:

python.exe -c "from multiprocessing.spawn import spawn_main; spawn_main(parent_pid=6872, pipe_handle=520)" --multiprocessing-fork

而在Python嵌入式冻结应用中,Windows上的子进程启动命令为:

my_program.exe --multiprocessing-fork parent_pid=8752 pipe_handle=1552

因此我认为需要在检测到--multiprocessing-fork命令行参数时,手动调用multiprocessing.spawn.spawn_main。以下是我的C++实现代码:

#include <Python.h>
#include <cstdlib>
#include <string>
#include <filesystem>
#include <string>
#include <vector>
#include <sstream>

static int multiprocess_main(int argc, char** argv)
{
    if (argc < 4)
    {
        fprintf(stderr, "Too little arguments to know how to run a python multiprocess.\n");
        return 1;
    }
    PyStatus status;

    PyConfig config;
    PyConfig_InitPythonConfig(&config);
    config.module_search_paths_set = 1; // sys.path will be: ['']

    std::filesystem::path executable_path(argv[0]);
    auto                  executable_directory = executable_path.parent_path().wstring();

    config.write_bytecode = 0;

    // In Python3.11, this will not work.
    status = PyConfig_SetString(&config, &config.stdlib_dir, executable_directory.c_str());
    if (PyStatus_Exception(status))
    {
        PyConfig_Clear(&config);
        fprintf(stderr, "%s", PyStatus_IsError(status) != 0 ? status.err_msg : "Failed to set sys._stdlib_dir.");
        return 1;
    }
    status = Py_InitializeFromConfig(&config);
    if (PyStatus_Exception(status))
    {
        PyConfig_Clear(&config);
        fprintf(stderr, "%s", PyStatus_IsError(status) != 0 ? status.err_msg : "Failed to initialize CPython from config.");
        return 1;
    }

    PyConfig_Clear(&config);

    int exitcode = 0;
    // Set sys._stdlib_dir manually
    PyObject* stdlib_dir_value;

    const auto* stdlibdir = executable_directory.c_str();

    stdlib_dir_value = PyUnicode_FromWideChar(stdlibdir, wcslen(stdlibdir));
    if (stdlib_dir_value == nullptr)
    {
        fprintf(stderr, "Could not convert directory name to unicode\n");
        PyErr_Print();
        return 1;
    }
    if (PySys_SetObject("_stdlib_dir", stdlib_dir_value) == -1)
    {
        fprintf(stderr, "Could not set sys._stdlib_dir\n");
        Py_DECREF(stdlib_dir_value);
        PyErr_Print();
        return 1;
    }
    Py_DECREF(stdlib_dir_value);
    // Set sys.frozen
    if (PySys_SetObject("frozen", Py_True) == -1)
    {
        fprintf(stderr, "Could not set sys.frozen\n");
        PyErr_Print();
        return 1;
    }

    // Set "--multiprocessing-fork" to sys.argv to make multiprocessing.spawn.is_forking work
    PyRun_SimpleString("import sys; sys.argv.append('--multiprocessing-fork')");

    // from multiprocessing.spawn import spawn_main; spawn_main(parent_pid=xxx, pipe_handle=yyy)
    // Currently only win32 is supported
    PyObject *spawn, *spawn_main, *spawnargs, *parent_pid, *pipe_handle, *result;
    for (int arg_idx = 2; arg_idx < argc; arg_idx++)
    {
        std::stringstream        argss(argv[arg_idx]);
        std::string              segment;
        std::vector<std::string> seglist;
        while (std::getline(argss, segment, '='))
        {
            seglist.push_back(segment);
        }
        if (seglist[0] == "parent_pid")
        {
            fprintf(stderr, "parent_pid=%s\n", seglist[1].c_str());
            parent_pid = PyLong_FromString(seglist[1].c_str(), nullptr, 0);
            if (parent_pid == nullptr)
            {
                fprintf(stderr, "Failed to parse parent_pid=%s while starting multiprocess\n", seglist[2].c_str());
                if (pipe_handle != nullptr)
                {
                    Py_DECREF(pipe_handle);
                }
                PyErr_Print();
                return 1;
            }
        }
        if (seglist[0] == "pipe_handle")
        {
            fprintf(stderr, "pipe_handle=%s\n", seglist[1].c_str());
            pipe_handle = PyLong_FromString(seglist[1].c_str(), nullptr, 0);
            if (pipe_handle == nullptr)
            {
                fprintf(stderr, "Failed to parse pipe_handle=%s while starting multiprocess\n", seglist[2].c_str());
                if (parent_pid != nullptr)
                {
                    Py_DECREF(parent_pid);
                }
                PyErr_Print();
                return 1;
            }
        }
    }
    if (pipe_handle == nullptr)
    {
        fprintf(stderr, "Could not parse pipe_handle while starting multiprocess\n");
        return 1;
    }
    spawn = PyImport_ImportModule("multiprocessing.spawn");
    if (spawn == nullptr)
    {
        fprintf(stderr, "Could not import multiprocessing.spawn\n");
        Py_DECREF(pipe_handle);
        if (parent_pid != nullptr)
        {
            Py_DECREF(parent_pid);
        }
        PyErr_Print();
        return 1;
    }
    spawn_main = PyObject_GetAttrString(spawn, "spawn_main");
    if (spawn_main == nullptr)
    {
        fprintf(stderr, "Could not access function: multiprocessing.spawn.spawn_main\n");
        Py_DECREF(spawn);
        Py_DECREF(pipe_handle);
        if (parent_pid != nullptr)
        {
            Py_DECREF(parent_pid);
        }
        PyErr_Print();
        return 1;
    }
    if (parent_pid != nullptr)
    {
        spawnargs = PyTuple_Pack(2, pipe_handle, parent_pid);
    }
    else
    {
        spawnargs = PyTuple_Pack(1, pipe_handle);
    }
    if (spawnargs == nullptr)
    {
        fprintf(stderr, "Could not create arguments for multiprocessing.spawn.spawn_main\n");
        Py_DECREF(spawn);
        Py_DECREF(spawn_main);
        Py_DECREF(pipe_handle);
        if (parent_pid != nullptr)
        {
            Py_DECREF(parent_pid);
        }
        PyErr_Print();
        return 1;
    }
    result = PyObject_Call(spawn_main, spawnargs, nullptr);
    Py_DECREF(spawn);
    Py_DECREF(spawn_main);
    Py_DECREF(spawnargs);
    Py_DECREF(pipe_handle);
    if (parent_pid != nullptr)
    {
        Py_DECREF(parent_pid);
    }
    if (result == nullptr)
    {
        PyErr_Print();
        return 1;
    }
    Py_DECREF(result);
    return exitcode;
}

int main(int argc, char** argv)
{
    if (argc >= 2 && strcmp(argv[1], "--multiprocessing-fork") == 0)
    {
        return multiprocess_main(argc, argv);
    }

    // Other normal stuffs
}

测试用的Python脚本如下:

import multiprocessing
from multiprocessing import spawn


def f():
    print("hello world!")


if __name__ == "__main__":
    multiprocessing.freeze_support()
    multiprocessing.Process(target=f).start()

但运行程序时,首次会输出如下错误:

Traceback (most recent call last):
  File "<frozen multiprocessing.spawn>", line 113, in spawn_main
  File "<frozen multiprocessing.reduction>", line 79, in duplicate
PermissionError: [WinError 5] Access is denied

每次重新编译生成可执行文件后首次运行都会出现该错误,之后无论运行多少次,都会出现如下错误:

Traceback (most recent call last):
  File "<frozen multiprocessing.spawn>", line 108, in spawn_main
OSError: [WinError 87] The parameter is incorrect

我不清楚问题出在哪里,希望能得到帮助,目标是在Windows系统中成功启动子进程。


内容的提问来源于stack exchange,提问作者Tac Uchiha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 08:07:06