如何使用Py_AddPendingCall

Cha*_*l72 5 c python cpython python-c-api python-3.x

我有一个嵌入式Python程序,它在C中的一个线程中运行.

当Python解释器切换线程上下文(让控制到另一个线程)时,我希望得到通知,以便我可以执行某些必要的操作.

这似乎Py_AddPendingCall正是我正在寻找的.但是,API文档在这个函数上非常简短,我对应该如何Py_AddPendingCall使用感到困惑.通过阅读文档,我的理解是:

  • 工作线程调用Py_AddPendingCall并分配处理函数.
  • 当Python解释器运行时,只要它控制到另一个线程,它就会调用处理函数
  • 处理程序本身在解释器线程中执行,并获取GIL

我用Google搜索了一些显示如何使用的代码Py_AddPendingCall,但我找不到任何东西.我自己尝试使用它根本行不通.永远不会调用处理程序.

我的工作线程代码:

#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>

const char* PYTHON_CODE = 
"while True:\n"
"   for i in range(0,10): print(i)\n"
"\n";

int handler(void* arg)
{
    printf("Pending Call invoked!\n");
    abort();
}

void* worker_thread(void*)
{
    PyGILState_STATE state = PyGILState_Ensure();
    int res = Py_AddPendingCall(&func, nullptr);
    cout << "Result: " << res << endl;
    PyRun_SimpleString(CODE);
    PyGILState_Release(state);

    return 0;
}

int main()
{
    Py_Initialize();
    PyEval_InitThreads();
    PyEval_ReleaseLock();

    pthread_t threads[4];
    for (int i = 0; i < 4; ++i)
        pthread_create(&threads[i], 0, worker_thread, 0);

    for (int i = 0; i < 4; ++i) pthread_join(threads[i], 0);

    Py_Finalize();
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,worker_thread在C中作为pthread工作线程调用.在这个测试中,我运行了4个工作线程,因此应该进行一些上下文切换.这无限循环,但永远不会调用挂起的调用处理程序.

那么,有人可以提供一个最小的工作示例,说明Py_AddPendingCall应该如何使用?

nne*_*neo 5

挂起调用仅在主线程上执行,主线程只能在执行Python代码时为挂起的调用提供服务.因此,主线程必须运行解释器循环才能为任何挂起的调用提供服务.来自Python/ceval.c:

Py_MakePendingCalls(void)
{
    ...

    /* only service pending calls on main thread */
    if (main_thread && PyThread_get_thread_ident() != main_thread)
        return 0;

    ...
}
Run Code Online (Sandbox Code Playgroud)

在嵌入场景中,"主线程"是任何线程调用PyEval_InitThreads(请注意,它也是第一次从Python代码启动线程时自动调用).由于您的主线程位于其中pthread_join,因此它不执行Python代码,因此不会调度挂起的调用.

另请注意,处理程序仅在将控制权交给主线程时执行 - 如果控制从一个工作程序转到另一个工作程序,则处理程序不会运行.因此,Py_MakePendingCalls可能不适合用于此任务的接口.