首页 > 解决方案 > 将多个 Python 子解释器嵌入到 C 程序中

问题描述

我正在编写一个生成多个 C 线程的 C 程序,每个线程有一个 Python 子解释器。子解释器不共享任何可变的 Python 变量,它们彼此隔离。(它们确实对从 C 程序中的 main() 函数公开的公共 PyObject(不可变)具有只读访问权限)。

这在 Python 3.7 或 3.8 中是否可能,而无需在子解释器之间共享 GIL?

这是我一直在尝试的伪代码:

void *spawnInterpreter(void* p) {
    …
    PyThreadState* save_tstate = PyThreadState_Swap(NULL);
    PyThreadState* tstate = Py_NewInterpreter();
    PyThreadState_Swap(save_tstate);

    //do some Python work (with variables that are NOT shared with other thread’s sub-interpreter
    PyRun_SimpleString( . . .);
    . . . 
}


int main() {
...
    pthread_create(&thread1, NULL, spawnInterpreter,  “in1”);
    pthread_create(&thread2, NULL, spawnInterpreter, "in2");
...
}

我可以让它在 3.6 中工作(无需获取 GIL 或PyThreadState在 C 线程中管理),但在 Python 3.7 中我得到:

[New Thread 0x7ffff5f78700 (LWP 16392)]
Fatal Python error: drop_gil: GIL is not locked

标签: pythonccpythonpython-c-api

解决方案


不幸的是,子解释器在 3.7 和 3.8 中仍然共享 GIL。这是我个人正在努力改变的事情。请参阅PEP 554我的多核 Python 项目。下周我还将在 PyCon 上发表演讲,详细介绍该主题。

我一直希望在 Python 3.8 中实现这一点,但在这一点上,它看起来更有可能在 3.9 中实现。主要挑战是 C-API 和 CPython 运行时不是线程安全的。虽然大多数 C-API 和运行时都可以切换到使用 per-interpreter GIL,但在这种情况下其他事情必须改变:

  • 一些进程全局资源必须在没有 GIL 的情况下更仔细地管理(例如环境变量、文件句柄)
  • 存在解释器必须继续共享的全局运行时状态,因此仍然必须通过全局锁来保护很多内容(尽管不需要阻塞 Python 字节码 eval 循环)
  • 一些全局运行时状态需要下移到每个解释器状态(例如 GC、内存分配器、警告)
  • 对象将需要严格按解释器进行(目前),因此 C-API 必须严格禁止对象跨越解释器边界
  • 非特定于解释器上下文的 C-API 部分必须更改为不再需要持有 GIL

这个问题很容易解决,但是在处理这些关键代码时需要时间来采取必要的谨慎措施。因此可能的目标是 3.9。


无论如何,我很感谢你在这里发帖。我的大部分努力都集中在对 Python 代码的影响上,而不是 C-API(例如嵌入器)。因此,有关我的项目如何通过 C-API 使用子解释器的反馈非常有帮助。例如,您提醒我的一件事是,通过 C-API 创建子解释器与 PEP 554 中的等效项略有不同。这需要更仔细地考虑。此外,PEP 554 几乎没有在 C-API 中公开任何添加内容。这可能没问题,但在短期内与来自 C-API 的渠道进行交互可能很有价值。


推荐阅读