Python C-Api线程问题

时间:2012-12-03 21:38:43

标签: python python-2.7

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

这在理论上意味着。

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

  1. 从c调用python lib的所有内容都是块状的(它们应该立即返回)
  2. python lib调用已注册的回调async(thread.start_new_thread(callback,args))。这不起作用(没有任何反应)。如果我将python代码更改为callback(args),那么它确实有效。
  3. 我做错了什么?我是否需要做些什么才能使多线程工作?

1 个答案:

答案 0 :(得分:5)

我有类似的情况。

初始工作流程

  1. 应用程序从C ++层开始
  2. C ++层在主线程中调用Python层中的函数
  3. 主线程中的Python图层函数创建一个事件线程
  4. 在Python层启动事件线程并返回C ++层
  5. 主循环从C ++层开始
  6. 如果需要,事件线程在C ++层中调用回调函数
  7. 从一开始,事件线程工作意外。我想这是因为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 */
    

    从上面的代码示例中,主线程始终拥有GIL,因此事件线程永远不会运行。为了克服这种情况,让主线程在调用PyGILState_Ensure()之前不获取GIL。因此,在调用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 ++层中调用回调函数