python多线程机制
Python中的线程从一开始就是操作系统的原生线程。而Python虚拟机也同样使用一个全局解释器锁(Global Interpreter Lock,GIL)来互斥线程多Python虚拟机的使用。
- GIL与线程调度
为了理解Pyhon为什么需要GIL,考虑这样的情形:假设有两个线程A B,在两个线程中,都同时保存着对内存中同一对象obj的引用,也就是说,这事obj->ob_refcnt的值为2.如果A销毁对obj的引用,显然,A将通过Py_DECREF调整obj的引用计数值。外面知道,py_DECREF的整个动作可以分为两个部分:
--obj ->ob_refcnt;
if(obj->ob_refcnt == 0) destory object and free memory.
如果A执行完第一个动作后,obj->ob_refcnt的值变为1,不幸的是,在这里时候线程调度机制将A挂起,唤醒了B。更为不幸的是,B同样也开始销毁对obj的引用。B完成第一个动作后,obj ->ob_refcnt为0,B是一个幸运儿,它没有被线程调度打断,而是顺利完成了接下来的第二个动作,将对象销毁,内存释放。好了吗,现在A又被重新唤醒,可现在已是物是人非,obj ->ob_refcnt已经被B减少到0,而不是当时的1.按照约定,A开始在一次地对已经销毁的对象进行对象销毁的内存释放动作。结局是什么?只有天知道………………
为了支持多线程机制,一个基本的要求就是需要实现不同线程对共享资源访问的互斥。Python也不例外,这正是引入GIL的根源所在。Python中的GIL是一个非常霸道的互斥实现,正如它的名字所暗示的,GIL是一个解释器(Interpreter)。也就是说,在一个线程拥有了解释器的访问权之后,其他的所有线程都必须等待它释放解释器的访问权,即使这些线程的下一条指令并不会互相影响。初看上去,这样的保护机制粒度太大了,我们似乎只需要将可能被多个线程共享的资源保护起来即可,对于不会被多个线程共享的资源,完全可以不用保护。实际上,在Python发展的历史中,的确出现过这样的解决方案,但令人惊奇的,这样的方案在单处理器上的多线程实现效率上却没有GIL的方案好,所以现在python中的多线程机制是在GIL的基础上实现的。
当然,这样的方案也就意味着,无论如何,在同一时间,只能有一个线程能访问python所提供的API。注意这里的同一时间对于单处理器是毫无意义的,因为单处理器的本质是不可能并行的,但是多处理器就完全不同了,同一时间,的确可以有多个线程独立运行,然而python的GIL限制了这样的情形,是的多处理器最终退化为单处理器,性能大打折扣。
2.
对于python而言,字节码解释器是python的核心所在,所以Python通过GIL来互斥不用线程对解释器的使用。
如图所示,A B C都需要使用解释器来执行字节码,以完成某种计算,但是在这之前,他们必须获得GIL,因为GIL把守这通往字节码解释器的大门。当A获得GIL之后,其他两个线程B C只能等待A释放GIL后,才能进入解释器,执行一些计算。
实际上,Python的GIL背后所保护的不仅仅是Python的解释器,同样还有Python的C API,在C/C++和Python的混合开发中,在涉及到原生线程和Python线程的相互协作时,也需要GIL进行互斥。
那么A在何时释放GIL呢?如果等到A使用完解释器之后,才释放GIL,这也就意味着,并行计算退化了为了串行的计算,毫无疑问,Python拥有一探线程的调度机制。
对于线程的调度机制而言,同操作系统的进程调度一样,最关键要解决两个问题:
- 在何时挂起当前的线程,选择处于等待状态的下一个线程?
- 在众多的处于等待状态的线程中,选择激活哪一个线程?
在python多线程的机制中,这两个问题是分别由不同的层次解决的。对于何时进行线程调度的问题,是由python自身决定的。考虑一下操作系统是如何进行进程的切换的。当一个进程执行了一段时间后,发生了时钟中断,操作系统响应时钟中断,并在这时开始进行进程的调度。同样,python中也是通过软件模拟了这样的时钟中断,来激活线程的调度。我们知道,python的字节码解释器的工作原理是按照指令的顺序一条一条的顺序执行,Python内部维护着一个数值,这个数值就是Python内部的时钟,如果这个数值为N,则意味着Python在执行了N条指令以后应该立即启动线程调度机制。
import sys
print sys.getcheckinterval()
上面代码的执行结果,Python默认是在执行了100条指令后启动线程调度机制。实际上,这个值不仅仅用来进行线程调度,在内部,Python也使用它来检查是否有异步的时间(envent)发生,需要处理。我们可以通过 sys.setcheckinterval() 来调节这个值。
那么究竟python会在众多等待线程中选择哪一个幸运儿呢?答案是,不知道。对于这个问题,Python完全没有插手,而是交给了底层的操作系统来解决。也就是说python借用了底层操作系统所提供的线程调度机制来决定下一个进入Python解释器的线程究竟是谁。
这一点至关重要,这就意味着Python中的线程实际上就是操作系统所支持的原生线程,并不是模拟出来的。Python中的多线程机制也是建立在操作系统的原生线程的基础之上,对应不同的操作系统,有不同的实现,然而最终,在不同的原生线程基础上,Python提供了一套统一的抽象机制,给Python的使用者一个非常简单而方便的多线程工具箱,这就是python中的两个Module:thread以及在其之上的threading。
python多线程机制的更多相关文章
- day-3 python多线程编程知识点汇总
python语言以容易入门,适合应用开发,编程简洁,第三方库多等等诸多优点,并吸引广大编程爱好者.但是也存在一个被熟知的性能瓶颈:python解释器引入GIL锁以后,多CPU场景下,也不再是并行方式运 ...
- Python GIL 多线程机制 (C source code)
最近阅读<Python源码剖析>对进程线程的封装解释: GIL,Global Interpreter Lock,对于python的多线程机制非常重要,其如何实现的?代码中实现如下: 指向一 ...
- 【python,threading】python多线程
使用多线程的方式 1. 函数式:使用threading模块threading.Thread(e.g target name parameters) import time,threading def ...
- <转>Python 多线程的单cpu与cpu上的多线程的区别
你对Python 多线程有所了解的话.那么你对python 多线程在单cpu意义上的多线程与多cpu上的多线程有着本质的区别,如果你对Python 多线程的相关知识想有更多的了解,你就可以浏览我们的文 ...
- Python多线程和Python的锁
Python多线程 Python中实现多线程有两种方式,一种基于_thread模块(在Python2.x版本中为thread模块,没有下划线)的start_new_thread()函数,另一种基于th ...
- 【跟我一起学Python吧】Python 多线程
其实自我感觉Python的多线程很类似于Java的多线程机制,但是比JAVA的多线程更灵活.在早期的Python多线程实现中,采用了thread模块.例如: from time import ctim ...
- 搞定python多线程和多进程
1 概念梳理: 1.1 线程 1.1.1 什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发 ...
- Python多线程练习(threading)
这几天学习python多线程的时候,试了几次thread模块和threading模块,发现thread模块非常的不好用.强烈不建议大家使用thread,建议使用threading模块,此模块对thre ...
- 进程,线程,GIL,Python多线程,生产者消费者模型都是什么鬼
1. 操作系统基本知识,进程,线程 CPU是计算机的核心,承担了所有的计算任务: 操作系统是计算机的管理者,它负责任务的调度.资源的分配和管理,统领整个计算机硬件:那么操作系统是如何进行任务调度的呢? ...
随机推荐
- javascript-图片横向无缝隙滚动
<style type="text/css"> <!-- ul,li,div{margin:0; padding:0; font-size:12px;} #dem ...
- 读懂IL代码(一)
以前刚开始学C#的时候,总有高手跟我说,去了解一下IL代码吧,看懂了你能更加清楚的知道你写出来的代码是如何运行互相调用的,可是那时候没去看,后来补的,其实感觉也不晚.刚开始看IL代码的时候,感觉非常吃 ...
- error C2220: warning treated as error - no 'object' file generated解决方法
error C2220: warning treated as error - no 'object' file generated 警讯视为错误 - 生成的对象文件 / WX告诉编译器将所有警告视为 ...
- get方式编码问题解决方案 转载
我们的内容使用GET方式发送,就会在URL后面带上内容,在游览器发来的请求经过了游览器的URI编码,发送到服务器这边,如果是struts2会经过拦截器进行URI解码,并且使用"iso8859 ...
- git学习小结 (笔记)
Modesty helps one to go forward, whereas conceit makes one lag behind. "虚心使人进步,骄傲使人落后" 注:本 ...
- Shell脚本——DHCP自动部署
详细说明参考: (三)跟我一起玩Linux网络服务:DHCP服务配置之主服务器配置 #! /bin/bash IPSAG="10.10.10" DNSIP="10.10. ...
- OS X Yosemite下安装Hadoop2.5.1伪分布式环境
最近开始学习Hadoop,一直使用的是公司配好的环境.用了一段时间后发现对Hadoop还是一知半解,故决定动手在本机上安装一个供学习研究使用.正好自己用的是mac,所以没啥说的,直接安装. 总体流程 ...
- 一个基于nodejs,支持http/https的中间人(MITM)代理,便于渗透测试和开发调试。
源码地址:https://github.com/wuchangming/node-mitmproxy node-mitmproxy node-mitmproxy是一个基于nodejs,支持http/h ...
- dedecms模版制作活动的折叠菜单
需要做成这种样式 url请求为这样: http://m03.com/plus/list.php?tid=19 这些菜单项都有对应的tid项,页面刷新后,应该将所有的菜单折叠起来,对于tid=19的菜单 ...
- 03:计算(a+b)/c的值
总时间限制: 1000ms 内存限制: 65536kB 描述 给定3个整数a.b.c,计算表达式(a+b)/c的值,/是整除运算. 输入 输入仅一行,包括三个整数a.b.c, 数与数之间以一个空格 ...