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

如何在pybind11中销毁Python解释器以实现C++循环调用Python函数

解决pybind11封装DLL被C#循环调用时的解释器重复初始化异常

问题根源

原C++代码中,py::finalize_interpreter();位于return result;之后,函数执行到return会直接退出,导致销毁解释器的代码永远不会执行。第一次调用后Python解释器持续存活,第二次调用py::initialize_interpreter()时,由于Python解释器在一个进程中只能初始化一次,因此抛出pybind11::error_already_set异常。

另外,C#代码中对参数a的MarshalAs(UnmanagedType.LPArray)是错误的,a是单个double值,并非数组,会导致参数传递异常。

最优解决方案:全局初始化一次解释器

由于Python解释器初始化和销毁的开销极大,且重复操作容易引发问题,最佳实践是在首次调用DLL函数时初始化一次解释器,后续调用直接复用,程序结束时再销毁。

修改后的C++代码(add.dll)

#include <pybind11/pybind11.h>
#include <iostream>
#include <stdexcept>

namespace py = pybind11;

// 全局保存Python加法函数,避免重复导入
static py::object g_addition_func;
static bool is_interpreter_initialized = false;

extern "C"
{
    __declspec(dllexport) double add(double a, double b)
    {
        // 首次调用时初始化Python解释器并导入目标函数
        if (!is_interpreter_initialized)
        {
            try
            {
                py::initialize_interpreter();
                py::module pyfile = py::module::import("pyfile");
                g_addition_func = pyfile.attr("addition");
                is_interpreter_initialized = true;
            }
            catch (const py::error_already_set& e)
            {
                std::cerr << "Python初始化失败:" << e.what() << std::endl;
                if (Py_IsInitialized())
                {
                    py::finalize_interpreter();
                }
                return -1.0; // 返回错误标记值
            }
        }

        // 调用Python函数计算结果
        try
        {
            double result = g_addition_func(a, b).cast<double>();
            std::cout << "C++侧计算结果:" << result << std::endl;
            return result;
        }
        catch (const py::error_already_set& e)
        {
            std::cerr << "Python函数调用失败:" << e.what() << std::endl;
            return -1.0;
        }
    }

    // 供C#调用,用于程序结束时销毁Python解释器
    __declspec(dllexport) void cleanup_python_env()
    {
        if (is_interpreter_initialized && Py_IsInitialized())
        {
            py::finalize_interpreter();
            g_addition_func = py::object();
            is_interpreter_initialized = false;
        }
    }
}

修改后的C#代码

using System;
using System.Runtime.InteropServices;

class Program
{
    // 修正参数MarshalAs错误,直接传递double值
    [DllImport("add.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern double add(double a, double b);

    // 导入销毁Python环境的函数
    [DllImport("add.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern void cleanup_python_env();
    
    static void Main(string[] args)
    {
        double a = 5.5;
        double b = 3.5;
        
        for(int i = 0; i < 5; i++)
        {
            double result = add(a, b);
            Console.WriteLine($"C#侧输出结果:{result}");
        }
        
        // 程序结束前销毁Python解释器
        cleanup_python_env();
    }
}

备选方案:每次调用都初始化/销毁解释器(不推荐)

如果因特殊需求必须每次调用都重新初始化解释器,需要确保py::finalize_interpreter()无论是否发生异常都能执行:

#include <pybind11/pybind11.h>
#include <iostream>

namespace py = pybind11;

extern "C"
{
    __declspec(dllexport) double add(double a, double b)
    {
        double result = -1.0;
        bool interpreter_initialized = false;

        try
        {
            py::initialize_interpreter();
            interpreter_initialized = true;
            
            result = py::module::import("pyfile").attr("addition")(a, b).cast<double>();
            std::cout << "C++侧计算结果:" << result << std::endl;
        }
        catch (const py::error_already_set& e)
        {
            std::cerr << "错误:" << e.what() << std::endl;
        }
        finally
        {
            // 确保解释器被销毁
            if (interpreter_initialized && Py_IsInitialized())
            {
                py::finalize_interpreter();
            }
        }

        return result;
    }         
}

注意:此方案每次调用都会初始化和销毁Python解释器,性能开销极大,且部分Python模块可能无法完全卸载,存在内存泄漏风险,仅在特殊场景下使用。


内容的提问来源于stack exchange,提问作者Chanukya N

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:35:16