使用Python和C API进行多线程编程

12
我有一个 C++ 程序,使用 C api 调用我的 Python 库。同时,Python库和C++代码都是多线程的。
特别地,C++程序的一个线程实例化了一个继承自 `threading.Thread` 的 Python 对象。我需要所有的 C++ 线程都能够调用该对象的方法。
从我的第一次尝试开始(我天真地只是在主线程中实例化对象,然后等待一段时间,再调用方法),我发现与创建的对象相关联的 Python 线程的执行在回到 C++ 程序时会停止。
如果执行保持在 Python 中(例如,如果我调用 `PyRun_SimpleString("time.sleep(5)");`),则 Python 线程的执行将在后台继续进行,直到等待结束并且执行返回到 C++ 为止,一切都正常。
显然我做错了什么。为了使我的 C++ 和 Python 都是多线程的,并能够很好地互相工作,我应该怎么做?由于我没有任何经验,请不要做任何假定!
2个回答

17
执行您尝试完成的操作的正确步骤如下:
  • 在主线程中:

    1. 使用 Py_Initialize* 初始化Python。
    2. 使用 PyEval_InitThreads() 初始化Python线程支持。
    3. 启动C++线程。

此时,主线程仍然持有GIL。

  • 在C++线程中:
    1. 使用 PyGILState_Ensure() 获取GIL。
    2. 创建一个新的Python线程对象并启动它。
    3. 使用 PyGILState_Release() 释放GIL。
    4. 睡眠、执行实用操作或退出线程。

由于主线程持有GIL,因此此线程将等待获取GIL。如果主线程调用Python API,则可以不时地释放GIL,使Python线程能够执行一段时间。

  • 回到主线程:
    1. 使用 PyEval_SaveThread() 释放GIL,启用线程运行。
    2. 在尝试使用其他Python调用之前,使用 PyEval_RestoreThread() 重新获取GIL。

我怀疑您缺少了最后一步-在主线程中释放GIL,使Python线程能够执行。

我有一个小而完整的示例,正是在此链接上做到了这一点。


0

当你从Python的threading.Thread回调时,可能没有解锁全局解释器锁(GIL)

如果你使用裸的Python C API,你可以在这里找到一些关于如何释放/获取GIL的文档。但是,如果你使用C++,我必须警告你,在C++代码中抛出任何异常可能会导致它崩溃。请看这里

通常情况下,任何运行时间过长的C++函数都应该在再次使用C Python API时解锁GIL并锁定。


很抱歉,但我似乎没有正确理解。这是我的做法:1)PyGILState_STATE gstate; gstate = PyGILState_Ensure();2)创建继承自 threading.Thread 的 Python 对象3)PyGILState_Release(gstate);4)睡眠几秒钟(在 C++ 中)在运行函数中,该对象应该每隔一段时间打印出一些东西,但显然只有前几毫秒才这样做。我做错了什么..? - Matteo Monti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接