5

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

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

看来这Py_AddPendingCall正是我正在寻找的。但是,API 文档对此功能非常简短,我对应该如何Py_AddPendingCall使用感到困惑。通过阅读文档,我的理解是:

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

我已经用谷歌搜索了显示如何使用的示例代码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();
}

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

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

4

1 回答 1

6

挂起的调用只在主线程上执行,主线程在执行 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;

    ...
}

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

还要注意,处理程序仅在控制权交给主线程时执行——如果控制权从一个工作人员转移到另一个工作人员,则处理程序不会运行。因此,Py_MakePendingCalls可能不是用于此任务的合适接口。

于 2012-09-16T16:51:42.190 回答