Python 3 中的 PyEval_InitThreads:如何/何时调用它? (故事继续令人作呕)

2023-12-13

基本上好像有massive对确切时间的困惑/模糊PyEval_InitThreads()应该被调用,以及需要哪些伴随的 API 调用。这Python 官方文档不幸的是非常含糊。已经有stackoverflow上有很多问题关于这个话题,事实上,我个人已经问了一个几乎相同的问题对于这个,所以如果它作为重复项关闭,我不会感到特别惊讶;但请考虑一下这个问题似乎没有明确的答案。 (遗憾的是,我的快速拨号上没有 Guido Van Rossum。)

首先,我们先定义一下问题的范围:我想做什么?嗯...我想用 C 语言编写一个 Python 扩展模块,它将:

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

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

无效 PyEval_InitThreads()

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

所以我在这里的理解是:

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

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

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

看起来很简单......但不幸的是,Python 3.2 文档also比如说PyEval_ReleaseLock已经已弃用。相反,我们应该使用PyEval_SaveThread为了释放 GIL:

PyThreadState* PyEval_SaveThread()

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

呃...好吧,所以我想 C 扩展模块需要说:

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


确实,这正是这个 stackoverflow 答案说。除非我实际上try实际上,当我导入扩展模块时,Python 解释器会立即出现段错误。好的。


好吧,现在我放弃官方 Python 文档并转向 Google。所以,这个随机博客声称您需要从扩展模块做的就是调用PyEval_InitThreads()。当然,文档声称PyEval_InitThreads()获得 GIL,事实上,快速检查源代码PyEval_InitThreads() in ceval.c表明它确实调用了内部函数take_gil(PyThreadState_GET());

So PyEval_InitThreads() 确实获得 GIL。我认为你绝对需要在调用后以某种方式释放 GILPyEval_InitThreads().   But how? PyEval_ReleaseLock()已弃用,并且PyEval_SaveThread()只是莫名其妙的段错误。

好吧...也许由于某种目前超出我理解的原因,C 扩展模块doesn't需要释放GIL。我尝试了...并且正如预期的那样,一旦另一个线程尝试获取 GIL(使用PyGILState_Ensure),程序因死锁而挂起。所以是的...你真的调用后需要释放GILPyEval_InitThreads().

那么问题又来了:调用后如何释放GILPyEval_InitThreads()?

更一般地说:C 扩展模块到底需要做什么才能从工作 C 线程安全地调用 Python 代码?


您的理解是正确的:调用PyEval_InitThreads除其他事项外,还获得了 GIL。在正确编写的 Python/C 应用程序中,这不是问题,因为 GIL 会及时自动或手动解锁。

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

这个答案的原始版本基本上到这里就结束了。但还有一件事需要考虑:嵌入设想。

嵌入 Python 时,主线程通常会初始化 Python 并继续执行其他非 Python 相关的任务。在这种情况下,没有什么可以自动地释放GIL,所以这必须由线程本身完成。这绝不是特定于调用的调用PyEval_InitThreads,预计所有 Python/C 代码使用获取的 GIL 进行调用。

例如,main()可能包含这样的代码:

Py_Initialize();
PyEval_InitThreads();

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

Py_Finalize();

如果您的代码手动创建线程,则它们需要在执行之前获取 GILanything与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);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python 3 中的 PyEval_InitThreads:如何/何时调用它? (故事继续令人作呕) 的相关文章

  • Python Nose 导入错误

    我似乎无法理解鼻子测试框架 https nose readthedocs org en latest 识别文件结构中测试脚本下方的模块 我已经设置了演示该问题的最简单的示例 下面我会解释一下 这是包文件结构 init py foo py t
  • 编辑 scikit-learn 决策树

    我想编辑 sklearn DecisionTree 例如改变条件或切割节点 叶子等 但似乎没有功能可以做到这一点 如果我可以导出到文件 编辑它以导入 如何编辑决策树 环境 Windows 10 python3 3 sklearn 0 17
  • Python,将迭代函数变成递归函数

    我创建了一个输出 4 3 2 1 0 1 2 3 4 的迭代函数 def bounce2 n s n for i in range n print n n n 1 if n lt 0 for i in range s 1 print n n
  • Scikit-learn 的内核 PCA:如何在 KPCA 中实现各向异性高斯内核或任何其他自定义内核?

    我目前正在使用Scikit learn 的 KPCA https scikit learn org stable modules generated sklearn decomposition KernelPCA html对我的数据集执行降
  • 为什么导入 pdb 时出现此错误? “模块”对象没有属性“ascii_letters”

    尝试调试我的代码 我正在导入库pdb import sys from subprocess import check call import pdb functions if name main Code 我收到此错误 File reg p
  • 如何从谷歌云存储桶读取音频文件并在datalab笔记本中使用ipd播放

    我想在数据实验室笔记本中播放我从谷歌云存储桶中读取的声音文件 这个怎么做 import numpy as np import IPython display as ipd import librosa import soundfile as
  • 字典中的列表,Python 中的循环

    我有以下代码 TYPES hotmail type hotmail lookup mixed dkim no signatures S Return Path email protected cdn cgi l email protecti
  • 如何使用循环将十进制转换为二进制?

    我想编写一个程序 将十进制数 0 到 9 转换为二进制数 我可以编写如何使用重复除法将十进制数转换为二进制数的代码 但是 我在创建一个以二进制格式打印十进制数字 0 到 9 的循环时遇到了麻烦 这是我的代码 number 0 remaind
  • 为 Networkx 图添加标题?

    我希望我的代码创建一个带有标题的图 使用下面的代码 可以创建绘图 但没有标题 有人可以告诉我我做错了什么吗 import pandas as pd import networkx as nx from networkx algorithms
  • 如何使用 Python 多处理避免在分叉进程中加载​​父模块

    当您创建一个Pool使用Python的进程multiprocessing 这些进程将分叉 父进程中的全局变量将显示在子进程中 如下面的问题所述 如何限制多处理进程的范围 https stackoverflow com questions 2
  • 如何从 Python 中指定运行程序的输入文件?

    我正在编写一个外部脚本 以通过笔记本电脑上的 Python mrjob 模块 而不是在 Amazon Elastic Compute Cloud 或任何大型集群上 运行 mapreduce 作业 我读自mrjob文档 http packag
  • Django - 电子邮件发送两次

    每当我使用如下所示的电子邮件设置从views py调用下面的方法时 电子邮件的两份副本都会发送给收件人 并且我收到如下所示的错误 def sendEmailBasic request msg EmailMessage Request Cal
  • Python正则表达式从字符串中获取浮点数

    我正在使用正则表达式来解析字符串中的浮点数 re findall a zA Z d d t 是我使用的代码 这段代码有问题 如果数字和任何字符之间没有空格 则不会解析该数字 例如 0 1 2 3 4 5 6 7 8 9 的预期输出为 0 1
  • 为什么“return self”返回 None ? [复制]

    这个问题在这里已经有答案了 我正在尝试获取链的顶部节点getTopParent 当我打印出来时self name 它确实打印出了父实例的名称 然而 当我回来时self 它返回 None 为什么是这样 class A def init sel
  • numpy.cov() 返回意外的输出

    我有一个 X 数据集 有 9 个特征和 683 行 683x9 我想获取这个 X 数据集和另一个与 X 具有相同形状的数据集的协方差矩阵 我使用np cov originalData generatedData rowvar False 代
  • 如何正确消除字典中的元素直到只剩下一个字符串

    我真的需要这方面的帮助 def get winner dict winner new dict for winner in dict winner first letter winner 0 value dict winner winner
  • 从另一个 python 脚本获取返回信息

    我在 Linux 上 我有一个 python 脚本 我想从另一个 python 脚本调用它 我不想将其作为模块导入 为了一层安全性 现在为了学术练习 因为我想弄清楚这一点 我实际上想让一个脚本使用 os system 或另一个类似的函数 并
  • 最小硬币找零问题——回溯

    我正在尝试用最少数量的硬币解决硬币找零问题 采用回溯法 我实际上已经完成了它 但我想添加一些选项 按其单位打印硬币数量 而不仅仅是总数 这是我下面的Python代码 def minimum coins coin list change mi
  • 在游戏中实现功能

    我在完成这部分作业时遇到了麻烦 我必须宣布游戏的获胜者 然后输入到函数中 输入所有 if 语句后 我必须创建一个函数def playGame 这必须包括 showRules user getUserChoice computer getCo
  • Python 枚举子集迭代

    我想迭代以下枚举的子集 class Items enum Enum item1 0 item2 1 item3 2 item4 3 item5 4 item6 5 item7 6 item8 7 说我想 for item in Items

随机推荐