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是计算机的核心,承担了所有的计算任务: 操作系统是计算机的管理者,它负责任务的调度.资源的分配和管理,统领整个计算机硬件:那么操作系统是如何进行任务调度的呢? ...
随机推荐
- Android studio混淆
看了一篇关于Android studio混淆的文章http://blog.csdn.net/qq_23547831/article/details/51581491,感觉有必要总结一个简单的混淆版本设 ...
- method=“post/get”
Form表单中method="post/get'的区别 Form提供了两种数据传输的方式——get和post.虽然它们都是数据的提交方式,但是在实际传输时确有很大的不同,并且可能会对数据 ...
- .getBoundingClientRect()
.getBoundingClientRect() 该方法获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置,他返回的是一个对象,即Object,该对象有4个属性:top,left,right, ...
- ICE学习第四步-----客户端请求服务器返回数据
这次我们来做一个例子,流程很简单:客户端向服务器发送一条指令,服务端接收到这条指令之后,向客户端发送数据库中查询到的数据,最终显示在DataGridView上. 根据上一篇文章介绍的Slice语法,我 ...
- PHP表单
二.PHP表单 1.PHP表单处理 welcome.html <html> <body> <form action="welcome.php" met ...
- php && 逻辑与运算符使用说明
例子:!defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc()); o(︶︿︶ ...
- PHP下编码转换函数mb_convert_encoding与iconv的使用说明
mb_convert_encoding这个函数是用来转换编码的. 不过英文一般不会存在编码问题,只有中文数据才会有这个问题.比如你用Zend Studio或Editplus写程序时,用的是gbk编码, ...
- window 配置 sendmail
从http://glob.com.au/sendmail/下载sendmail.zip 解压sendmail.zip到目录下(最好使用短路径,长路径会导致问题的出现),我安装的路径是: E:\wamp ...
- smarty中判断一个变量是否存在于一个数组中或是否存在于一个字符串中?
smarty支持php的系统函数可以直接使用{if in_array($str, $arr) || strpos($str, $string)} yes {else} no{/if}
- PhotoSwipe.js 相册展示插件学习
PhotoSwipe.js官网:http://photoswipe.com/,在这个网站上可以下载到PhotoSwipe的文件以及相关的例子. 这个组件主要是用来展示图片.相册用的,还是很实用的. 一 ...