如何使用Python C / API包装多线程C ++库?

时间:2011-03-24 08:10:32

标签: c++ python pthreads

这是一个有点长的问题,但我希望我能清楚地表达出来。

我正在尝试使用Python / C API包装C ++库。主库,比如 mylib ,有自己的对象系统(它类似于另一种语言的解释器),并通过 Id 唯一地标识其环境中的每个对象。它在init()函数中创建多个线程,并在不同的线程上执行不同的操作(比如在一个线程上创建对象并在另一个线程中解释命令)。 现在我试图把它包装在两个层面:

  1. 我使用mylib中对象的 Id 创建了一个 Dummy 类。 Dummy构造函数实际上在mylib中调用一个函数来创建一个新对象并存储其Id。 Dummy类中的其他方法类似地调用mylib中的等效函数。这不使用Python / C API。

  2. 我创建了mylibmodule.cpp,它使用Python / C API提供将从Python解释器调用的函数。

    我在init()中调用了mylib的PyMODINIT_FUNC init_mylib()函数。

    我的代码函数如下:

    static PyObject * py_new_Dummy(PyObject* self, PyObject *args){
    
      // ... process arguments
    
      return reinterpret_cast<PyObject*>(new Dummy);
    
    }
    
  3. 请注意,Dummy构造函数调用mylib中的函数,这些函数在使用pthreads创建的线程上执行。

    我把它编译成_mylib.so,我有一个mylib.py:

     import _mylib
    
     class MyClass(obj):
    
         def __init__(self, *args)
    
             self.__ptr = _mylib.py_new_Dummy()
    

    现在解决实际问题:我可以在Python解释器中导入mylib,但是一旦我尝试:

    a = MyClass(some_args)
    

    我遇到了分段错误。 gdb回溯显示

      

    编程接收信号SIGSEGV,分段故障。

         pthread_mutex_lock.c上的

    __ pthread_mutex_lock(mutex = 0x0):50

    更有趣的是,如果我禁用在mylib代码中生成多个线程(仍与pthreads链接),我可以创建MyClass实例,但是在退出Python解释器时会出现分段违规。

    Python文档中的“Thin Ice”部分(http://docs.python.org/extending/)没有启发我。我想知道是否应该在mylibmodule.cpp中的所有Python C / API调用周围使用PyGILState_Ensure和PyGILState_Release。或者应该是Py_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS?

    有人可以帮忙吗?是否有关于Python与pthreads完全匹配的确切文档?

2 个答案:

答案 0 :(得分:3)

根据您的描述,它听起来根本不是一个线程问题:您声称在不使用Python API的情况下定义Dummy类,但这意味着Dummy个实例是不是PyObjects ,所以reinterpret_cast会做错事。您不能通过实例化C ++类来创建PyObjects;你需要与Python的对象系统一起使用并创建一个合适的PyType结构和一个PyObject结构并正确地初始化它们。您还需要确保您的引用是正确的。

一旦你有了这个排序,关于线程要记住的主要事情是任何涉及Python对象的调用或使用任何Python API的任何调用(除了获取GIL的函数除外)都必须获得GIL < / em>的。如果C ++库中的任何线程尝试回调Python代码或触摸Python对象,则需要将访问权限包含在PyGILState_Ensure / PyGILState_Release中。

答案 1 :(得分:0)

谢谢托马斯指出红鲱鱼。问题出在C ++端的线程初始化中。 是的,它不需要任何GIL操作,因为没有其他C ++线程访问Python C / API。