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

多线程C++环境嵌入Python时TaskA执行挂死问题求助(Windows 10+MSVC+Python3.13.5)

多线程C++环境嵌入Python时TaskA执行挂死问题求助(Windows 10+MSVC+Python3.13.5)

看起来你遇到了在Taskflow多线程任务里操作Python GIL导致的挂死问题,我来帮你拆解下原因和解决办法:

问题环境

  • 系统:Windows 10
  • 编译工具:MSVC
  • Python版本:3.13.5
  • 依赖库:Taskflow(仅头文件)

核心挂死原因

你的代码里有一个容易被忽略的关键细节:调用PyEval_InitThreads()后,主线程会自动持有GIL,但你没有主动释放它。

Taskflow的工作线程在执行TaskA时,PyGILState_Ensure()会尝试获取GIL,可此时主线程一直攥着GIL不放,导致工作线程无限等待,最终整个程序彻底挂死——连Ctrl+C都无法中断,因为主线程阻塞在Taskflow的等待逻辑上,工作线程又卡在GIL获取的阻塞中,形成了死锁。

解决方案

只需要在PyEval_InitThreads()之后,立即释放主线程持有的GIL,让工作线程能够正常获取GIL即可。

关键修改点

在Python初始化代码段中添加释放GIL的逻辑:

Py_Initialize();
PyEval_InitThreads();

// 新增:释放主线程持有的GIL(PyEval_InitThreads会自动获取GIL,必须主动释放)
PyEval_ReleaseThread(PyThreadState_Get());

if (!Py_IsInitialized()) {
    std::cerr << "Python failed to initialize\n";
    return 1;
}

也可以用Python官方提供的宏来实现相同效果:

PyEval_InitThreads();
Py_BEGIN_ALLOW_THREADS
// 宏会自动释放GIL,后续主线程代码将在无GIL状态下运行
Py_END_ALLOW_THREADS

修改后的完整代码示例

#include <iostream>
#include <taskflow/taskflow.hpp> // Taskflow is header-only
#define PY_SSIZE_T_CLEAN
#include <Python.h>

int main(int argc, char* argv[]) {
    wchar_t pythonHome[] = L".venv";
    Py_SetPythonHome(pythonHome);

    Py_Initialize();
    PyEval_InitThreads();
    // 释放主线程GIL,让工作线程可正常获取
    PyEval_ReleaseThread(PyThreadState_Get());

    if (!Py_IsInitialized()) {
        std::cerr << "Python failed to initialize\n";
        return 1;
    }

    // Set up Python paths
    PyRun_SimpleString(
        "import sys\n"
        "sys.path.insert(0, '.venv/Lib')\n"
        "sys.path.insert(0, '.venv/Lib/site-packages')\n"
    );

    // Test script execution
    PyRun_SimpleString(
        "from time import time, ctime\n"
        "print('Today is', ctime(time()))\n"
    );

    PyObject* main = PyImport_AddModule("__main__");
    PyObject* globals = PyModule_GetDict(main);

    tf::Executor executor;
    tf::Taskflow taskflow;

    auto [A, B, C, D] = taskflow.emplace( // create four tasks
        [] () {
            std::cout << "TaskA\n";
            // 正确成对管理GIL
            PyGILState_STATE gstate = PyGILState_Ensure();
            // 这里可添加具体Python操作(如调用函数、创建对象等)
            PyGILState_Release(gstate);
        },
        [] () { std::cout << "TaskB\n"; },
        [] () { std::cout << "TaskC\n"; },
        [] () { std::cout << "TaskD\n"; }
    );

    A.precede(B, C); // A runs before B and C
    D.succeed(B, C); // D runs after B and C

    executor.run(taskflow).wait();

    // 终结Python前,主线程重新获取GIL确保清理正常
    PyGILState_STATE gstate = PyGILState_Ensure();
    if (Py_FinalizeEx() < 0) {
        return 120;
    }
    PyGILState_Release(gstate);

    return 0;
}

额外注意事项

  • GIL管理规范:所有非Python创建的C++线程,只要调用Python API,必须用PyGILState_Ensure()获取GIL,用PyGILState_Release()释放,确保成对调用。
  • Python终结注意:调用Py_FinalizeEx()前,要确保所有线程都已释放GIL,建议在主线程重新获取GIL后再执行终结操作,避免崩溃或资源泄漏。
  • sys.path优化:如果程序工作目录可能变化,建议用绝对路径设置sys.path,或通过Python的site模块自动处理,避免找不到模块的问题。

这样修改后,TaskA的工作线程就能顺利获取GIL,不会再出现挂死的情况了。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.09 03:03:13