Python C-Api线程问题

Ale*_*ßen 4 python python-2.7

我正在编写一个C程序,它使用用python编写的网络库.我用python C api嵌入了python lib.该库发送所有请求异步,并在请求完成时通过信号通知我.

这在理论上意味着.

实际上我有两个与线程相关的问题:

  1. 从c调用python库的所有调用都是块状的(它们应该立即返回)
  2. python lib调用已注册的回调async(thread.start_new_thread(callback,args)).这不起作用(没有任何反应).如果我将python代码更改为回调(args),那么它确实有效.

我做错了什么?有什么东西要做多线程工作吗?

Bru*_*uce 5

我有类似的情况.

初步工作流程

  1. 应用程序从C++层开始
  2. C++层在主线程中调用Python层中的函数
  3. 主线程中的Python层函数创建一个事件线程
  4. 在Python层启动事件线程并返回C++层
  5. 主循环从C++层开始
  6. 如果需要,事件线程在C++层中调用回调函数

从一开始,事件线程就意外地工作了.我想这是因为GIL来自我遇到的情况所以我试图从GIL解决这个问题.这是我的解决方案.

分析

首先,从PyEval_InitThreads中的注释,

仅存在主线程时,不需要GIL操作....因此,最初不会创建锁....

因此,如果需要多线程,则PyEval_InitThreads()必须在主线程中调用.我PyEval_InitThreads()之前打过电话Py_Initialize().现在GIL已初始化,主线程获取GIL.

其次,每次从C++层调用Python函数之前,PyGILState_Ensure()都会调用它来获取GIL.另外,在调用Python函数之后,PyGILState_Release(state)调用它回到之前的GIL状态.结果,在步骤2之前,PyGILState_Ensure()调用,并在步骤4之后,PyGILState_Release(state)调用.

但有一个问题.从PyGILState_EnsurePyGILState_Release,这两个函数是保存当前GIL状态以获取GIL并恢复以前的GIL状态以释放GIL.但是,PyEval_InitThreads()在主线程调用之后,主线程肯定拥有GIL.主线程中的GIL状态如下:

/* main thread owns GIL by PyEval_InitThreads */

state = PyGILState_Ensure();
/* main thread owns GIL by PyGILState_Ensure */

...
/* invoke Python function */
...

PyGILState_Release(state);
/* main thread owns GIL due to go back to previous state */
Run Code Online (Sandbox Code Playgroud)

从上面的代码示例中,主线程总是拥有GIL,因此事件线程永远不会运行.要克服这种情况,让主线程在调用之前不要获取GIL PyGILState_Ensure().因此,在调用之后PyGILState_Release(state),主线程可以释放GIL以让事件线程运行.因此,当GIL初始化时,应立即在主线程中释放GIL.

PyEval_SaveThread()是用的.从PyEval_SaveThread,

释放全局解释器锁(如果已创建并启用了线程支持)并将线程状态重置为NULL,...

通过这样做,嵌入Python与多线程工作.

修改后的工作流程

  1. 应用程序从C++层开始
  2. PyEval_InitThreads(); 启用多线程
  3. save = PyEval_SaveThread(); 在主线程中释放GIL
  4. state = PyGILState_Ensure(); 在主线程中获取GIL
  5. C++层在主线程中调用Python层中的函数
  6. 主线程中的Python层函数创建一个事件线程
  7. 在Python层启动事件线程并返回C++层
  8. PyGILState_Release(state); 在主线程中释放GIL
  9. 主循环从C++层开始
  10. 如果需要,事件线程在C++层中调用回调函数