我有类似的情况。
初始工作流程
1. 应用程序从C++层开始
2. C++层调用Python层中的函数(在主线程中)
3. Python层中的主线程函数创建了一个事件线程
4. 在Python层中启动事件线程并返回到C++层
5. C++层中开始主循环
6. 如果需要,事件线程会在C++层中调用回调函数
从一开始,事件线程的工作效果出人意料。 我猜这是由于我遇到的情况是GIL引起的,因此我尝试从GIL解决这个问题。 这是我的解决方案。
分析
首先,从
PyEval_InitThreads的注释中可以看出,
当仅存在主线程时,不需要进行GIL操作。 因此,在初始情况下不会创建锁。 ...
因此,必须在主线程中调用
PyEval_InitThreads()
,以便进行多线程操作。 我在
Py_Initialize()
之前调用了
PyEval_InitThreads()
。 现在GIL已初始化,并且主线程获取了GIL。
其次,在每次从C++层调用Python函数之前,都会调用
PyGILState_Ensure()
来获取GIL。 在调用Python函数之后,会调用
PyGILState_Release(state)
来返回上一个GIL状态。 因此,在步骤2之前,调用
PyGILState_Ensure()
,并在步骤4之后调用
PyGILState_Release(state)
。
但是有一个问题。 从
PyGILState_Ensure和
PyGILState_Release中可以看出,这两个函数是为了保存当前的GIL状态以获取GIL,并恢复先前的GIL状态以释放GIL。 然而,在主线程中调用
PyEval_InitThreads()
后,主线程肯定拥有GIL。 主线程中的GIL状态如下:
state = PyGILState_Ensure();
...
...
PyGILState_Release(state);
从上面的代码示例可以看出,主线程始终拥有GIL,因此事件线程永远不会运行。为了克服这种情况,在调用
PyGILState_Ensure()
之前,让主线程不获取GIL。因此,在调用
PyGILState_Release(state)
后,主线程可以释放GIL以让事件线程运行。因此,在初始化GIL时,应立即在主线程中释放GIL。
这里使用了
PyEval_SaveThread()
。从
PyEval_SaveThread中,释放全局解释器锁(如果已创建并启用线程支持)并将线程状态重置为NULL,...通过这样做,嵌入Python多线程工作。
修改后的工作流程如下:
- 应用程序从C++层开始
PyEval_InitThreads();
启用多线程
save = PyEval_SaveThread();
在主线程中释放GIL
state = PyGILState_Ensure();
在主线程中获取GIL
- C++层在主线程中调用Python层的函数
- Python层函数在主线程中创建一个事件线程
- 在Python层启动事件线程并返回C++层
PyGILState_Release(state);
在主线程中释放GIL
- C++层开始主循环
- 如果需要,事件线程调用C++层的回调函数
PyGILState_Ensure
函数? - user4815162342