如何使用 pybind11 在 C++ 线程内调用 Python 函数作为回调

2023-11-27

我设计了一个 C++ 系统,它从在单独线程中运行的过程调用用户定义的回调。简化版system.hpp看起来像这样:

#pragma once

#include <atomic>
#include <chrono>
#include <functional>
#include <thread>

class System
{
public:
  using Callback = std::function<void(int)>;
  System(): t_(), cb_(), stop_(true) {}
  ~System()
  {
    stop();
  }
  bool start()
  {
    if (t_.joinable()) return false;
    stop_ = false;
    t_ = std::thread([this]()
    {
      while (!stop_)
      {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        if (cb_) cb_(1234);
      }
    });
    return true;
  }
  bool stop()
  {
    if (!t_.joinable()) return false;
    stop_ = true;
    t_.join();
    return true;
  }
  bool registerCallback(Callback cb)
  {
    if (t_.joinable()) return false;
    cb_ = cb;
    return true;
  }

private:
  std::thread t_;
  Callback cb_;
  std::atomic_bool stop_;
};

它工作得很好,可以用这个简短的例子进行测试main.cpp:

#include <iostream>
#include "system.hpp"

int g_counter = 0;

void foo(int i)
{
  std::cout << i << std::endl;
  g_counter++;
}

int main()
{
  System s;
  s.registerCallback(foo);
  s.start();
  while (g_counter < 3)
  {
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  }
  s.stop();
  return 0;
}

这将输出1234几次然后就会停止。然而,我在尝试为我的创建 python 绑定时遇到了问题System。如果我将 python 函数注册为回调,我的程序将在调用后死锁System::stop。我对这个话题进行了一些调查,看来我面临着这个问题GIL。可重现的例子:

binding.cpp:

#include "pybind11/functional.h"
#include "pybind11/pybind11.h"

#include "system.hpp"

namespace py = pybind11;

PYBIND11_MODULE(mysystembinding, m) {
  py::class_<System>(m, "System")
    .def(py::init<>())
    .def("start", &System::start)
    .def("stop", &System::stop)
    .def("registerCallback", &System::registerCallback);
}

蟒蛇脚本:

#!/usr/bin/env python

import mysystembinding
import time

g_counter = 0

def foo(i):
  global g_counter
  print(i)
  g_counter = g_counter + 1

s = mysystembinding.System()
s.registerCallback(foo)
s.start()
while g_counter < 3:
  time.sleep(1)
s.stop()

我已阅读pybind11 文档关于在 C++ 端获取或释放 GIL 的可能性的部分。然而,我未能摆脱我的案例中出现的僵局:

PYBIND11_MODULE(mysystembinding, m) {
  py::class_<System>(m, "System")
    .def(py::init<>())
    .def("start", &System::start)
    .def("stop", &System::stop)
    .def("registerCallback", [](System* s, System::Callback cb)
      {
        s->registerCallback([cb](int i)
        {
          // py::gil_scoped_acquire acquire;
          // py::gil_scoped_release release;
          cb(i);
        });
      });
}

如果我打电话py::gil_scoped_acquire acquire;在调用回调之前,无论如何都会发生死锁。 如果我打电话py::gil_scoped_release release;在调用回调之前,我得到

致命的 Python 错误:PyEval_SaveThread:NULL tstate

我应该怎么做才能将 python 函数注册为回调并避免死锁?


谢谢这次讨论和许多其他资源(1, 2, 3)我发现保护启动和加入 C++ 线程的函数gil_scoped_release似乎解决了问题:

PYBIND11_MODULE(mysystembinding, m) {
  py::class_<System>(m, "System")
    .def(py::init<>())
    .def("start", &System::start, py::call_guard<py::gil_scoped_release>())
    .def("stop", &System::stop, py::call_guard<py::gil_scoped_release>())
    .def("registerCallback", &System::registerCallback);
}

显然,发生死锁是因为 python 在调用负责 C++ 线程操作的绑定时持有锁。我仍然不确定我的推理是否正确,所以我将不胜感激任何专家的评论。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 pybind11 在 C++ 线程内调用 Python 函数作为回调 的相关文章

  • 如何使用 Celery 多工作人员启用自动缩放?

    命令celery worker A proj autoscale 10 1 loglevel info启动具有自动缩放功能的工作人员 当创建多个工人时 me mypc projects x celery multi start mywork
  • 检查 RoutedEvent 是否有任何处理程序

    我有一个自定义 Button 类 当单击它时 打开特定窗口 它总是执行相同的操作 我添加了一个可以在按钮的 XAML 中分配的 Click 事件 就像常规按钮一样 当它被单击时 我想执行 Click 事件处理程序 如果已分配 否则我想执行默
  • 使用 WSGI 在 Windows XAMPP 中设置 Python 路径

    我正在 Webfaction 上设置实时服务器的开发版本 在本地计算机上的虚拟 Apache 服务器环境 运行没有任何错误 中运行 Django 应用程序 XP 使用 Python 2 6 运行 XAMPP Lite 我可以提交更改通过 G
  • 在seaborn中对箱线图x轴进行排序

    我的数据框round data看起来像这样 error username task path 0 0 02 n49vq14uhvy93i5uw33tf7s1ei07vngozrzlsr6q6cnh8w 39 png 1 0 10 n49vq
  • 将标量添加到特征矩阵(向量)

    我刚刚开始使用 Eigen 库 无法理解如何向所有矩阵成员添加标量值 假设我有一个矩阵 Eigen Matrix3Xf mtx Eigen Matrix3Xf Ones 3 4 mtx mtx 1 main cxx 104 13 error
  • 如何分析组合的 python 和 c 代码

    我有一个由多个 python 脚本组成的应用程序 其中一些脚本正在调用 C 代码 该应用程序现在的运行速度比以前慢得多 因此我想对其进行分析以查看问题所在 是否有工具 软件包或只是一种分析此类应用程序的方法 有一个工具可以将 python
  • 如何在c的case语句中使用省略号?

    CASE expr no commas ELLIPSIS expr no commas 我在c的语法规则中看到了这样的规则 但是当我尝试重现它时 int test float i switch i case 1 3 printf hi 它失
  • 根据列索引重命名 Dataframe 列

    是否有内置函数可以按索引重命名 pandas 数据框 我以为我知道列标题的名称 但事实证明第二列中有一些十六进制字符 根据我接收数据的方式 我将来可能会在第 2 列中遇到这个问题 因此我无法将这些特定的十六进制字符硬编码到 datafram
  • Django Rest Framework POST 更新(如果存在或创建)

    我是 DRF 的新手 我阅读了 API 文档 也许这是显而易见的 但我找不到一个方便的方法来做到这一点 我有一个Answer与 a 具有一对一关系的对象Question 在前端 我曾经使用 POST 方法来创建发送到的答案api answe
  • 更换壳牌管道[重复]

    这个问题在这里已经有答案了 在 subprocess 模块的 Python 2 7 文档中 我找到了以下片段 p1 Popen dmesg stdout PIPE p2 Popen grep hda stdin p1 stdout stdo
  • 我可以让 ungetc 取消阻止阻塞的 fgetc 调用吗?

    我想在收到 SIGUSR1 后使用 ungetc 将 A 字符重新填充到标准输入中 想象一下我有充分的理由这样做 调用 foo 时 stdin 中的阻塞读取不会被收到信号时的 ungetc 调用中断 虽然我没想到它会按原样工作 但我想知道是
  • 在 scipy 中创建新的发行版

    我试图根据我拥有的一些数据创建一个分布 然后从该分布中随机抽取 这是我所拥有的 from scipy import stats import numpy def getDistribution data kernel stats gauss
  • 沿轴 0 重复 scipy csr 稀疏矩阵

    我想重复 scipy csr 稀疏矩阵的行 但是当我尝试调用 numpy 的重复方法时 它只是将稀疏矩阵视为对象 并且只会将其作为 ndarray 中的对象重复 我浏览了文档 但找不到任何实用程序来重复 scipy csr 稀疏矩阵的行 我
  • 跨多个域的 ASP.NET 会话

    是否有合适的 NET 解决方案来在多个域上提供持久服务器会话 即 如果该网站的用户在 www site1 com 下登录 他们也将在 www site2 com 下登录 安全是我们正在开发的程序的一个问题 Thanks 它是否需要在会话中
  • 如何更改matplotlib中双头注释的头大小?

    Below figure shows the plot of which arrow head is very small 我尝试了下面的代码 但它不起作用 它说 引发 AttributeError 未知属性 s k 属性错误 未知属性头宽
  • 如何在 DropDownList 中保留空格 - ASP.net MVC Razor 视图

    我在视图中通过以下方式绑定我的模型 问题是我的项目文本是格式化文本 单词之间有空格 如下所示 123 First 234 00 123 AnotherItem 234 00 123 Second 234 00 我想保留此项目文本中的空格 即
  • 使用 SERVER_NAME 时出现 Flask 404

    在我的 Flask 配置中 我将 SERVER NAME 设置为 app example com 之类的域 我这样做是因为我需要使用url for with external网址 如果未设置 SERVER NAME Flask 会认为服务器
  • 将 char[][] 转换为 char** 会导致段错误吗?

    好吧 我的 C 有点生疏了 但我想我应该用 C 来做我的下一个 小 项目 这样我就可以对其进行抛光 并且我已经有不到 20 行的段错误了 这是我的完整代码 define ROWS 4 define COLS 4 char main map
  • 在python中对列表列表执行行总和和列总和

    我想用python计算矩阵的行和和列和 但是 由于信息安全要求 我无法使用任何外部库 因此 为了创建矩阵 我使用了列表列表 如下所示 matrix 0 for x in range 5 for y in range 5 for pos in
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959

随机推荐