我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何正确区分Python线程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何正确区分Python线程

这篇文章给大家介绍如何正确区分Python线程,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

在Python语言中Python线程可以从这里开始与主线程对GIL的竞争,在t_bootstrap中,申请完了GIL,也就是说子线程也就获得了GIL,使其始终保存着活动线程的状态对象。

当PyEval_AcquireThread结束之后,子线程也就获得了GIL,并且做好了一切执行的准备。接下来子线程通过PyEval_ CallObjectWithKeywords,将最终调用我们已经非常熟悉的PyEval_EvalFrameEx。

也就是Python的字节码执行引擎。传递进PyEval_CallObjectWithKeywords的boot->func是一PyFunctionObject对象,正是therad1.py中定义的threadProc编译后的结果。在PyEval_CallObjectWithKeywords结束之后,子线程将释放GIL,并完成销毁线程的所有扫尾工作,到了这里,子线程就结束了。

从t_bootstrap的代码看上去,似乎子线程会一直执行,直到子线程的所有计算都完成,才会通过PyThreadState_DeleteCurrent释放GIL。如此一来,那主线程岂非一直都会处于等待GIL的状态?如果真是这样,那Python线程显然就不可能支持多线程机制了。

实际上在PyEval_EvalFrameEx中,图15-2中显示的Python内部维护的那个模拟时钟中断会不断地激活线程的调度机制,在子线程和主线程之间不断地进行切换。从而真正实现多线程机制,当然,这一点我们将在后面详细剖析。现在我们感兴趣的是子线程在PyEval_AcquireThreade中到底做了什么。

到这里,了解了PyEval_AcquireThread,似乎创建线程的机制都清晰了。但实际上,有一个非常重要的机制——线程状态保护机制——隐藏在了一个毫不起眼的地方:PyThreadState_New。

[threadmodule.c]   static PyObject* thread_PyThread_start_new_thread(PyObject *self, PyObject     *fargs)   {       PyObject *func, *args, *keyw = NULL;       struct bootstate *boot;       long ident;       PyArg_UnpackTuple(fargs, "start_new_thread", 2, 3, &func, &args, &keyw);       //[1]:创建bootstate结构       boot = PyMem_NEW(struct bootstate, 1);       boot->interp = PyThreadState_GET()->interp;       boot->funcfunc = func;       boot->argsargs = args;       boot->keywkeyw = keyw;       //[2]:初始化多线程环境       PyEval_InitThreads();        //[3]:创建线程       ident = PyThread_start_new_thread(t_bootstrap, (void*) boot);       return PyInt_FromLong(ident);   [thread.c]      static size_t _pythread_stacksize = 0;   [thread_nt.h]   long PyThread_start_new_thread(void (*func)(void *), void *arg)   {       unsigned long rv;       callobj obj;       obj.id = -1;           obj.func = func;       obj.arg = arg;       obj.done = CreateSemaphore(NULL, 0, 1, NULL);       rv = _beginthread(bootstrap, _pythread_stacksize, &obj);        if (rv == (unsigned long)-1) {           //创建raw thread失败           obj.id = -1;       }       else {           WaitForSingleObject(obj.done, INFINITE);       }       CloseHandle((HANDLE)obj.done);       return obj.id;   }

这个机制对于理解Python线程的创建和维护是非常关键的。要剖析线程状态的保护机制,我们首先需要回顾一下线程状态。在Python中,每一个Python线程都会有一个线程状态对象与之关联。

在线程状态对象中,记录了每一个线程所独有的一些信息。实际上,在剖析Python的初始化过程时,我们曾经见过这个对象。每一个线程对应的线程状态对象都保存着这个线程当前的PyFrameObject对象,线程的id这样一些信息。有时候,线程是需要访问这些信息的。

比如考虑一个最简单的情形,在某种情况下,每个线程都需要访问线程状态对象中所保存的thread_id信息,显然,线程A获得的应该是A的thread_id,线程B亦然。倘若线程A获得的是B的thread_id,那就坏菜了。这就意味着Python线程内部必须有一套机制,这套机制与操作系统管理进程的机制非常类似。

我们知道,在操作系统从进程A切换到进程B时,首先会保存进程A的上下文环境,再进行切换;当从进程B切换回进程A时,又会恢复进程A的上下文环境,这样就保证了进程A始终是在属于自己的上下文环境中运行。

这里的线程状态对象就等同于进程的上下文,Python同样会有一套存储、恢复线程状态对象的机制。同时,在Python内部,维护着一个全局变量:PyThreadState * _PyThread- State_Current。

当前活动线程所对应的线程状态对象就保存在这个变量里,当Python调度线程时,会将被激活的线程所对应的线程状态对象赋给_PyThreadState_Current,使其始终保存着活动线程的状态对象。

这就引出了这样的一个问题:Python如何在调度进程时,获得被激活线程对应的状态对象?Python内部会通过一个单向链表来管理所有的Python线程的状态对象,当需要寻找一个线程对应的状态对象时。

  • 初次接触Python部署问题解析

  • 强大快捷的Python操作语言全解析

  • 对Python 调试器丰富资源介绍

  • 对Python交互式技巧总结之谈

  • 如何正确认识Python 源文件

就遍历这个链表,搜索其对应的状态对象。在此后的描述中,我们将这个链表称为“状态对象链表”。下面我们来看一看实现这个机制的关键数据结构在Python中,对于这个状态对象链表的访问,不必在GIL的保护下进行。

因为对于这个状态对象链表,Python线程会创建一个独立的锁,专职对状态对象链表进行保护。这个锁的创建是在Python进行初始化的时候完成的。PyThread_create_key将创建一个新的key。注意,这里的key都是一个整数,而且,当PyThread_create_key***次被调用时(在_PyGILState_Init中的调用正是***次调用)。

会通过PyThread_allcate_lock创建一个keymutex。根据我们前面的分析,这个keymutex实际上和GIL一样,都是一个PNRMUTEX结构体,而在这个结构体中,维护着一个Win32下的Event内核对象。这个keymutex的功能就是用来互斥对状态对象链表的访问。

在_PyGILState_Init中,创建的新key被Python维护的全局变量autoTLSkey接收,其中的TLS是Thread Local Store的缩写,这个autoTLSkey将用作Python保存所有线程的状态对象的一个参数。的key值。也就是说,状态对象列表中所有key结构体中的key值都会是autoTLSkey。哎,那位看官说了,你看PyThread_create_key返回的是nkeys的递增后的值啊。

就是说每create一次,得到的结果都是不同的,怎么能说所有的key都是一样的呢?事实上,在整个Python的源码中,PyThread_create_key只在_PyGILState_Init中被调用了,而这个_PyGILState_Init只会在Python运行时环境初始化时调用一次。

关于如何正确区分Python线程就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何正确区分Python线程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何正确区分Python线程

这篇文章给大家介绍如何正确区分Python线程,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。在Python语言中Python线程可以从这里开始与主线程对GIL的竞争,在t_bootstrap中,申请完了GIL,也就是说
2023-06-17

如何正确使用Android线程详解

前言 对于移动开发者来说,“将耗时的任务放到子线程去执行,以保证UI线程的流畅性”是线程编程的第一金科玉律,但这条铁则往往也是UI线程不怎么流畅的主因。我们在督促自己更多的使用线程的同时,还需要时刻提醒自己怎么避免线程失控。 多线程编程之所
2022-06-06

php时区不正确如何解决

本文小编为大家详细介绍“php时区不正确如何解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“php时区不正确如何解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。解决方法:1、在php.ini文件中,设置“d
2023-07-05

win10系统安装固态硬盘如何正确分区

这篇文章主要为大家展示了“win10系统安装固态硬盘如何正确分区”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“win10系统安装固态硬盘如何正确分区”这篇文章吧。一、固态硬盘的容量相比于机械硬盘
2023-06-27

python如何正确使用yield

目录生成器nextsendthrowclose使用场景大集合的生成简化代码结构协程与并发总结生成器如果在一个方法内,包含了 yield 关键字,那么这个函数就是一个「生成器」。 生成器其实就是一个特殊的迭代器,它可以像迭代器那样,迭代输出方
2022-06-02

如何正确使用Python命令

本篇内容主要讲解“如何正确使用Python命令”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何正确使用Python命令”吧!我想你最常用到的 Python 命令就是运行 Python 脚本文件
2023-06-16

如何正确进行Python编写

这篇文章给大家介绍如何正确进行Python编写,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Python编写的Zope是开放源代码领域使用最广泛和最容易理解的内容管理框架,而这正是Microsoft .NET的CLR技
2023-06-17

如何正确运行python命令

本篇内容介绍了“如何正确运行python命令”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Python已经成为全球最受欢迎的编程语言之一。原
2023-06-16

Python中print如何正确使用

Python中print如何正确使用,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Python print会对输出的文本做自动的编码转换,而文件对象的write方法就不会做,因
2023-06-17

如何正确理解python装饰器

目录一、闭包二、装饰器三、带参数的装饰器四、类装饰器一、闭包 要想了解装饰器,首先要了解一个概念,闭包。什么是闭包,一句话说就是,在函数中再嵌套一个函数,并且引用外部函数的变量,这就是一个闭包了。光说没有概念,直接上一个例子。def out
2022-06-02

Windows7-如何正确卸载程序

要正确卸载程序,您可以按照以下步骤操作:1. 打开控制面板:点击开始菜单,然后选择“控制面板”。2. 在控制面板中找到“程序”或“程序和功能”选项,并点击打开。3. 在“程序和功能”窗口中,您将看到列出的所有已安装程序的列表。4. 在该列表
2023-09-08

如何正确的使用Python闭包

如何正确的使用Python闭包?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。一、Python 中的作用域规则和嵌套函数每当执行一个函数时,就会创建一个新的局部命
2023-06-15

github如何正确提问(技巧分享)

在使用GitHub的过程中,你可能会遇到各种各样的问题,这时候如何正确提问是很重要的。正确的提问方式能够帮助你更快地解决问题,也能让其他人更愿意帮助你。本文将介绍GitHub中如何正确提问的技巧。搜索已有问题在提问之前,首先要搜索是否有其他
2023-10-22

python如何正确的操作字符串

目录0x01 字符串(string)引号转义拼接字符串长字符串索引( indexing)运算符in创建列表切片赋值0x02 字符串格式化模板字符串字符串方法format0x03 如何设置格式字段名转换标志格式说明0x04 字符串方法常量填充
2022-06-02

如何在python中正确的使用goto

今天就跟大家聊聊有关如何在python中正确的使用goto,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和
2023-06-07

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录