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

Pybind11使用py::keep_alive<1,2>无法保活传入C++的Python对象

问题根源

你的问题核心是Python侧派生的TestEvent实例的生命周期没有和C侧存储的EventPointer正确关联,py::keep_alive<1,2>的配置逻辑没错,但缺少配套的类型绑定配置,且C侧的存储方式没有主动持有Python对象引用:

  1. 如果你的EventPointer是std::shared_ptr<Event>,你在绑定Event基类时没有显式指定std::shared_ptr作为持有者类型,pybind不会将C侧的智能指针计数和Python对象的GC引用计数关联,哪怕C侧shared_ptr计数不为0,Python GC还是会回收临时创建的TestEvent实例。
  2. 你用keep_alive<1,2>的语义是让EventManager实例(参数1)持有事件对象(参数2)的引用,这种方式会让事件对象的生命周期和EventManager完全绑定,如果事件消费后没有手动解除引用会造成内存泄漏,且如果Event类没有正确绑定多态属性,保活配置不会生效。
  3. 你C侧队列只存储了EventPointer(指向基类的智能指针),没有持有Python派生对象的引用,Python GC无法感知C侧还在使用这个对象,会在QueueEvent调用结束后直接回收临时TestEvent实例。

解决方案

步骤1:修正Event基类的绑定配置

绑定基类时显式指定持有者为std::shared_ptr<Event>,如果允许Python继承Event,必须配套实现trampoline类:

// 先实现trampoline类,用于Python派生类的虚函数转发
class PyEvent : public Event {
public:
    using Event::Event;
    // 重写所有你需要在Python侧实现的虚函数,例如GetEventType
    EventType GetEventType() const override {
        PYBIND11_OVERRIDE_PURE(EventType, Event, GetEventType);
    }
};

// 绑定时指定holder和trampoline
py::class_<Event, PyEvent, std::shared_ptr<Event>>(m, "Event")
    .def(py::init<>())
    // 其他Event类的方法绑定
    ;

步骤2:调整事件存储逻辑,主动持有Python对象引用

如果希望事件消费后自动释放引用,不要依赖keep_alive和EventManager绑定生命周期,直接修改C++侧的队列存储结构,同时保存智能指针和Python对象引用:

// 队列存储类型修改为pair,同时持有事件指针和Python对象引用
std::queue<std::pair<EventPointer, py::object>> mEventQueue;

void EventManager::QueueEvent(const EventPointer pEvent) {
    auto _foundList = mEventListenersDelegatesMap.find(pEvent->GetEventType());
    if (_foundList != mEventListenersDelegatesMap.end()){
        // 构造时自动增加Python对象的引用计数
        mEventQueue.Push(std::make_pair(pEvent, py::cast(pEvent)));
        return;
    }
}

// 消费事件的逻辑,消费完成后pair销毁自动减少引用计数
void ProcessEventQueue() {
    while(!mEventQueue.empty()) {
        auto [pEvent, pyRef] = mEventQueue.front();
        mEventQueue.pop();
        // 执行事件触发逻辑
        TriggerEvent(pEvent);
        // 函数退出后pyRef销毁,自动解除Python对象引用
    }
}

步骤3:优化绑定配置(可选)

如果还是要使用keep_alive,给方法显式指定参数名,避免索引错位:

.def("TriggerEvent", &EventManager::TriggerEvent, 
    py::arg("pEvent"),
    py::keep_alive<1, 2>())
.def("QueueEvent", &EventManager::QueueEvent, 
    py::arg("pEvent"),
    py::keep_alive<1, 2>())

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 13:36:05