将多个Python子解释器嵌入到C程序中

12

我正在编写一个C程序,它会生成多个C线程,每个线程都有一个Python子解释器。这些子解释器不共享任何可变的Python变量,它们是相互独立的。(它们确实可以只读访问从C程序中的main()函数暴露出来的公共PyObject(不可变))。

在Python 3.7或3.8中,是否可以做到这一点而不共享sub-interpreters之间的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中使其运作(无需在C线程中获取GIL或管理PyThreadState),但在Python 3.7中我收到以下错误:

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

你得到的“致命Python错误”看起来像是一个回归。在Python 3.7运行时代码中有一些相关的变化,但我不认为它们会改变任何行为。我强烈建议在bugs.python.org上开一个问题(并在这里发布链接)。 - Eric Snow
我将提供有关子解释器不共享GIL的答案。 - Eric Snow
PyThreadState_Swap() 必须在 GIL 被持有的情况下调用,并且不会释放它。 Py_NewInterpreter() 也是如此。因此,在调用 PyRun_SimpleString() 时,您正在持有 GIL。一切都应该没问题。如果我必须猜测,我会建议它与 GILState API 有关,该 API 与子解释器不兼容(请参见 https://bugs.python.org/issue10915 和 https://bugs.python.org/issue15751)。 - Eric Snow
你有致命错误的堆栈跟踪吗?在没有持有GIL的情况下,drop_gil()被调用在哪里?它可能在Python/ceval.c中,但具体是哪个调用呢? - Eric Snow
1个回答

19

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

我的希望是使其成为Python 3.8的可能,但目前看来更有可能在3.9实现。主要的挑战在于C-API和CPython运行时不是线程安全的。虽然大部分C-API和运行时可以切换到使用每个解释器的GIL,但在这种情况下,其他一些事情必须发生改变:

  • 某些进程全局资源必须在没有GIL的情况下更加小心地管理(例如env vars, file handles)
  • 存在全局运行时状态,解释器必须继续共享这些状态,因此需要用全局锁来保护(虽然这个锁不需要阻塞Python字节码评估循环)
  • 其中一些全局运行时状态需要下移到每个解释器状态(例如GC,内存分配器,警告)
  • 对象将需要严格按解释器区分(目前),因此C-API必须严格禁止对象跨解释器边界传递
  • 非特定于解释器上下文的C-API部分必须更改,以不再需要持有GIL。
  • 问题是可以解决的,但在处理如此关键的代码时需要花费一定的时间来进行必要的细心处理。因此,预计目标版本为3.9。


    无论如何,我很感谢你在这里发布。我的大部分工作重点是Python代码的影响,而不是C-API(例如嵌入式设备)。因此,有关我的项目如何通过C-API与使用子解释器相关的反馈是非常有帮助的。例如,你提醒我创建子解释器的C-API略有不同于PEP 554中相应的方法。那需要更加仔细地考虑。此外,PEP 554在C-API中几乎没有公开其添加内容。这可能没问题,但从C-API与通道交互可能会短期内有价值。


    我认为那将会是一个PIL(或者PIGIL?!),而不是GIL ¯\(ツ)/¯。@eric:很高兴听到在GIL方面有一些工作正在进行 :) - Marti Nito
    1
    Eric,你的PEP 554指的是3.10版本,但在3.10.0rc1的发布说明中没有任何内容。目标是什么? - Eric

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接