在阻塞的C函数前可以释放GIL吗?

4 投票
2 回答
1240 浏览
提问于 2025-04-16 12:46

我正在封装一个C语言的函数,这个函数会进行一个阻塞操作(也就是会等待的操作,比如说选择某些东西),然后处理接收到的消息。我理解的是,当一个C语言的函数会阻塞时,正确的做法是这样调用它,这样其他线程才能继续运行:

Py_BEGIN_ALLOW_THREADS                                                  
blocking_function();
Py_END_ALLOW_THREADS

不过,这个函数有一个参数是回调指针。这个回调是在处理由C函数预处理的接收到的消息时被调用的。我已经成功地把这个回调封装成一个函数,里面调用了PyEval_CallObject(),这样我就可以传入一个Python的回调函数。

现在我在添加线程支持,我在想是否可以同时做到:

  • 在调用这个阻塞操作之前释放全局解释器锁(GIL)。
  • 让这个阻塞操作安全地回调到Python解释器。

这样做会不会引发问题?如果会,有没有解决办法?

谢谢。

2 个回答

1

如果我用一个全局变量来保存PyEval_SaveThread的结果,然后在回调函数中用这个全局变量来调用PyEval_RestoreThread,这样就能正常工作了。我只需要想办法更干净地通过回调的上下文指针把这个全局变量传递到我的代码里。

7

我几个月前用过这些API函数,记得不是很清楚,但我觉得这段代码能解决你的问题。我假设你使用的是2.x版本(3.x可能会有所不同):

PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

/* Make your call to PyEval_CallObject() here (and any other PY API calls). */

PyGILState_Release(gstate);

(以上内容摘自:Python C/API文档

这基本上是Py_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS宏的反向操作。在这两个宏中,你是释放全局解释器锁(GIL),而在PyGILState函数中,你则是获取全局解释器锁。

撰写回答