当前位置: 首页 > 面试题库 >

Python 3中的PyEval_InitThreads:如何/何时调用它?

张德佑
2023-03-14
问题内容

基本上,在什么时候应该准确调用什么以及需要什么伴随的API调用方面似乎存在 巨大的 困惑/模糊性
PyEval_InitThreads()

。不幸的是,Python的官方文档非常模糊。关于这个话题,已经有很多关于stackoverflow的问题,因此,如果将其作为副本关闭,我不会感到特别惊讶。但是请考虑一下,这个问题似乎没有确定的答案。(可悲的是,我没有快速拨号上的Guido VanRossum。)

首先,让我们在这里定义问题的范围: 我想做什么? 好吧…我想用C写一个Python扩展模块,它将:

  1. 使用pthreadC语言中的API生成工作线程
  2. 从这些C线程中调用Python回调

好的,让我们从Python文档本身开始。在
Python的3.2文档
说:

无效PyEval_InitThreads()

初始化并获取全局解释器锁。在创建第二个线程或进行任何其他线程操作(例如PyEval_ReleaseThread(tstate))之前,应在主线程中调用它。在调用PyEval_SaveThread()或PyEval_RestoreThread()之前不需要它。

所以我的理解是:

  1. 产生线程的任何C扩展模块都必须PyEval_InitThreads()在产生任何其他线程之前从主线程调用
  2. 调用会PyEval_InitThreads锁定GIL

因此,常识告诉我们,任何创建线程的C扩展模块都必须调用PyEval_InitThreads(),然后释放全局解释器锁。好吧,看起来很简单。因此,
表面看来 ,所需的只是以下代码:

PyEval_InitThreads(); /* initialize threading and acquire GIL */
PyEval_ReleaseLock(); /* Release GIL */

似乎很容易…但是不幸的是,Python 3.2文档 说它 PyEval_ReleaseLock 已被
弃用
。相反,我们应该使用
PyEval_SaveThread
它来发布GIL:

PyThreadState * PyEval_SaveThread()

释放全局解释器锁(如果已创建并启用了线程支持),然后将线程状态重置为NULL,返回先前的线程状态(非NULL)。如果已创建锁,则当前线程必须已获取它。

嗯…好吧,所以我猜一个C扩展模块需要说:

PyEval_InitThreads();
PyThreadState* st = PyEval_SaveThread();

确实,这正是
这个stackoverflow答案 所说的。除非我在实践中实际 尝试
过,否则在导入扩展模块时,Python解释器会立即出现段错误。真好

好的,现在我放弃正式的Python文档,转向Google。因此,
这个随机博客
声称您需要从扩展模块执行的所有操作是PyEval_InitThreads()。当然,文档声称PyEval_InitThreads()获得了GIL,实际上,

快速检查PyEval_InitThreads()in的源代码ceval.c
表明它确实调用了内部功能。take_gil(PyThreadState_GET());

因此PyEval_InitThreads() 一定要 获得GIL。我认为那您绝对需要在致电后以某种方式释放GIL
PyEval_InitThreads()但是如何?
PyEval_ReleaseLock()已过时,并且出现PyEval_SaveThread()了莫名其妙的段错误。

好了…所以也许出于某种原因这是目前我无法理解,一个C扩展模块 并不
需要释放GIL。我尝试了一下……并且,正如预期的那样,一旦另一个线程尝试获取GIL(使用
PyGILState_Ensure
),程序就会从死锁中挂起。是的…您 真的 需要在致电后释放GIL PyEval_InitThreads()

同样,问题是:
在调用后如何释放GILPyEval_InitThreads()

更笼统地说: 为了能够从辅助C线程安全地调用Python代码,C扩展模块到底需要做什么?


问题答案:

您的理解是正确的:调用PyEval_InitThreads确实可以获取GIL。在正确编写的Python /
C应用程序中,这不是问题,因为GIL将自动或手动及时解锁。

如果主线程继续运行Python代码,则没有什么特别的事情,因为Python解释器将在执行了许多指令后自动放弃GIL(允许另一个线程来获取它,它将再次放弃它,依此类推)上)。另外,每当Python要调用阻塞系统调用时(例如,从网络读取或写入文件),它将在调用周围释放GIL。

这个答案的原始版本到此为止。但是还有另外一件事要考虑: 嵌入 方案。

嵌入Python时,主线程通常会初始化Python并继续执行其他与Python不相关的任务。在那种情况下,没有什么东西会 自动
释放GIL,因此必须由线程本身完成。这绝不是特定于call的调用PyEval_InitThreads,它是在使用获取的GIL调用的所有Python /
C代码中所期望的。

例如,main()可能包含以下代码:

Py_Initialize();
PyEval_InitThreads();

Py_BEGIN_ALLOW_THREADS
... call the non-Python part of the application here ...
Py_END_ALLOW_THREADS

Py_Finalize();

如果您的代码是手动创建线程的,则它们需要先获取GIL,然后再进行与Python相关的 任何操作
,甚至简单到Py_INCREF。为此,请使用以下命令:

// Acquire the GIL
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();

... call Python code here ...

// Release the GIL. No Python API allowed beyond this point.
PyGILState_Release(gstate);


 类似资料:
  • 我正在使用Python 3.2。1并且我无法导入模块。我使用可以工作,但我不能将它与的一起使用,如下所示: 我得到以下错误: 当我写时,它说

  • 问题内容: 现在,我使用一个静态布尔值来告诉初始化何时发生。有没有更简单的方法知道我已经调用了initialize? 谢谢!!! 解决了!!!!非常感谢您的评论。您需要在扩展应用程序的类中初始化解析,然后将其作为应用程序(而不是其他活动)添加到清单文件中。 :) 这是我使用Parse的课程: 这是我的android清单文件 问题答案: 创建一个应用程序类,然后在onCreate中初始化解析。 在此

  • 本文向大家介绍如何用Python3实现Dictionary,包括了如何用Python3实现Dictionary的使用技巧和注意事项,需要的朋友参考一下 python中的字典是一种数据结构,可将键映射到作为键值对的值。它们是经常使用的数据结构之一,并具有许多有趣的属性。通过将它们括在一对大括号中来呈现它们,如下所示。 字典中的元素或键值对用单引号表示,并用冒号分隔。 创建字典 我们通过分配以键形式编

  • 我有相当简单的片段与ListView,CursorLoader和CursorAdapter。一切都在单个活动中(只是切换片段)。我的问题是onLoadFinished()在某些情况下会根据initLoader()调用的位置调用两次。这些情况是: 配置更改(旋转屏幕等) 使用FragmentTransaction,用另一个片段替换当前片段,然后返回(弹出backstack)。在本例中,一个列表项的详

  • 问题内容: 我想按需取消正在运行的命令,为此,我正在尝试,目前正在尝试这样做: https://play.golang.org/p/0JTD9HKvyad 我面临的问题是被调用了,但是进程没有被杀死,我的猜测是主线程首先退出,并且不等待正确终止命令,主要是因为如果我在末尾使用a 在的退出功能/杀死正在运行的命令。 关于如何在退出不使用?之前确保命令已被杀死的任何想法?成功杀死命令后,可以在通道中使

  • 问题内容: 什么时候 叫?我有一个活动,需要在调用onMeasure之后执行一项操作。 我的问题与此处未回答的问题相同。在查看文档指出onMeasure当被称为requestLayout()被调用时,它显然是对自身视图调用时它认为是不能再目前的范围内配合。 但是,这不能告诉我何时我的活动可以假设我的视图已被测量。我已使用此代码将ImageView扩展为TouchImageView。建议在这里使用o