使用 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。
执行您要执行的操作的正确步骤顺序是:
在主线程中:
- 使用
Py_Initialize*
初始化Python。
- 使用
PyEval_InitThreads()
初始化 Python 线程支持。
- 启动 C++ 线程。
此时主线程还持有GIL
- 在 C++ 线程中:
- 使用
PyGILState_Ensure()
获取 GIL。
- 创建一个新的 Python 线程对象并启动它。
- 使用
PyGILState_Release()
释放GIL。
- 睡觉,做一些有用的事情或退出线程。
因为主线程持有GIL,所以该线程将等待获取GIL。如果主线程调用 Python API 它可能会不时释放 GIL 允许 Python 线程执行一会儿。
- 回到主线程:
- 发布 GIL,启用线程 运行 使用
PyEval_SaveThread()
- 在尝试使用其他 Python 调用之前,请使用
PyEval_RestoreThread()
重新获取 GIL
我怀疑你错过了最后一步——释放主线程中的 GIL,让 Python 线程执行。
我有一个小而完整的例子,在 this link.
我有一个 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。
执行您要执行的操作的正确步骤顺序是:
在主线程中:
- 使用
Py_Initialize*
初始化Python。 - 使用
PyEval_InitThreads()
初始化 Python 线程支持。 - 启动 C++ 线程。
- 使用
此时主线程还持有GIL
- 在 C++ 线程中:
- 使用
PyGILState_Ensure()
获取 GIL。 - 创建一个新的 Python 线程对象并启动它。
- 使用
PyGILState_Release()
释放GIL。 - 睡觉,做一些有用的事情或退出线程。
- 使用
因为主线程持有GIL,所以该线程将等待获取GIL。如果主线程调用 Python API 它可能会不时释放 GIL 允许 Python 线程执行一会儿。
- 回到主线程:
- 发布 GIL,启用线程 运行 使用
PyEval_SaveThread()
- 在尝试使用其他 Python 调用之前,请使用
PyEval_RestoreThread()
重新获取 GIL
- 发布 GIL,启用线程 运行 使用
我怀疑你错过了最后一步——释放主线程中的 GIL,让 Python 线程执行。
我有一个小而完整的例子,在 this link.