使用 Python 和 C api 的多线程

Multithreading with Python and C api

我有一个 C++ 程序,它使用 C api 来使用我的 Python 库。 Python 库和 C++ 代码都是多线程的。

特别是,C++ 程序的一个线程实例化了一个继承自 threading.Thread 的 Python 对象。我需要我的所有 C++ 线程都能够调用该对象上的方法。

从我第一次尝试开始(我天真地只是从主线程实例化对象,然后等待一段时间,然后调用方法)我注意到与对象关联的 Python 线程的执行只是一旦执行返回到 C++ 程序,created 就会停止。

如果执行停留在 Python(例如,如果我调用 PyRun_SimpleString("time.sleep(5)");),Python 线程的执行将在后台继续并且一切正常,直到等待结束并且执行回到 C++。

我显然做错了什么。我应该怎么做才能使 我的 C++ 和 Python 多线程并且能够很好地相互工作?我以前没有该领域的经验,所以请不要假设任何事情!

当您从 python 的 threading.Thread 回调时,您可能没有解锁 Global Interpreter Lock

好吧,如果您使用的是裸 python 的 C API,那么您有 some documentation here, about how to release/acquire the GIL. But while using C++, I must warn you that it might broke down upon any exceptions throwing in your C++ code. See here

一般来说,任何运行时间过长的 C++ 函数都应该解锁 GIL 并锁定,只要它再次使用 C Python API。

执行您要执行的操作的正确步骤顺序是:

  • 在主线程中:

    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. 发布 GIL,启用线程 运行 使用 PyEval_SaveThread()
    2. 在尝试使用其他 Python 调用之前,请使用 PyEval_RestoreThread()
    3. 重新获取 GIL

我怀疑你错过了最后一步——释放主线程中的 GIL,让 Python 线程执行。

我有一个小而完整的例子,在 this link.