Python中的线程从一开始就是操作系统的原生线程。而Python虚拟机也同样使用一个全局解释器锁(Global Interpreter Lock,GIL)来互斥线程多Python虚拟机的使用。

  1. 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多线程机制的更多相关文章

  1. day-3 python多线程编程知识点汇总

    python语言以容易入门,适合应用开发,编程简洁,第三方库多等等诸多优点,并吸引广大编程爱好者.但是也存在一个被熟知的性能瓶颈:python解释器引入GIL锁以后,多CPU场景下,也不再是并行方式运 ...

  2. Python GIL 多线程机制 (C source code)

    最近阅读<Python源码剖析>对进程线程的封装解释: GIL,Global Interpreter Lock,对于python的多线程机制非常重要,其如何实现的?代码中实现如下: 指向一 ...

  3. 【python,threading】python多线程

    使用多线程的方式 1.  函数式:使用threading模块threading.Thread(e.g target name parameters) import time,threading def ...

  4. <转>Python 多线程的单cpu与cpu上的多线程的区别

    你对Python 多线程有所了解的话.那么你对python 多线程在单cpu意义上的多线程与多cpu上的多线程有着本质的区别,如果你对Python 多线程的相关知识想有更多的了解,你就可以浏览我们的文 ...

  5. Python多线程和Python的锁

    Python多线程 Python中实现多线程有两种方式,一种基于_thread模块(在Python2.x版本中为thread模块,没有下划线)的start_new_thread()函数,另一种基于th ...

  6. 【跟我一起学Python吧】Python 多线程

    其实自我感觉Python的多线程很类似于Java的多线程机制,但是比JAVA的多线程更灵活.在早期的Python多线程实现中,采用了thread模块.例如: from time import ctim ...

  7. 搞定python多线程和多进程

    1 概念梳理: 1.1 线程 1.1.1 什么是线程 线程是操作系统能够进行运算调度的最小单位.它被包含在进程之中,是进程中的实际运作单位.一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发 ...

  8. Python多线程练习(threading)

    这几天学习python多线程的时候,试了几次thread模块和threading模块,发现thread模块非常的不好用.强烈不建议大家使用thread,建议使用threading模块,此模块对thre ...

  9. 进程,线程,GIL,Python多线程,生产者消费者模型都是什么鬼

    1. 操作系统基本知识,进程,线程 CPU是计算机的核心,承担了所有的计算任务: 操作系统是计算机的管理者,它负责任务的调度.资源的分配和管理,统领整个计算机硬件:那么操作系统是如何进行任务调度的呢? ...

随机推荐

  1. rdlc报表

    也是第一次接触报表这个东西.现在在我理解,报表无非就是两个内容,格式和数据. 格式没有多少了解,就记录了,以后再续.数据的绑定和结果的显示是怎么实现的呢? 今天的主角就是rdlc这个文件和Report ...

  2. wpf 调用线程必须为sta 因为许多ui组件都需要

    解决 办法 public void SomeMethod() { var task = System.Windows.Application.Current.Dispatcher.BeginInvok ...

  3. INSERT INTO SELECT FROM 这语句怎么用

    如果两表字段相同,则可以直接这样用. insert into table_a select * from table_b 如果两表字段不同,a表需要b中的某几个字段即可,则可以如下使用: insert ...

  4. Oracle实用技巧

    一. ORACLE SQL PLUS 使用技巧: ----①查找重复记录: SELECT DRAWING, DSNOFROM EM5_PIPE_PREFABWHERE ROWID!= (SELECT ...

  5. java基础学习总结01

    在过去的一周我们先学习了JAVA的一些基础知识和基础语法,基础知识包括:指令.程序.计算机语言.机器语言.高级语言的概念和特点,java的概念.平台.组建等,以及代码的编译过程,代码的开发流程.基础语 ...

  6. ICE学习第三步-----Slice语言

    ICE:Slice语言(一)-编译 Introduce简介 Slice(Specification language for ice)是分离对象和对象的实现的基础的抽象机制.Slice在客户端和服务器 ...

  7. mac 自带 php 验证码 不显示

    curl -s http://php-osx.liip.ch/install.sh | bash -s 5.5 在命令行中输入上面   ,最后5.5是php版本号 自动安装FreeType ----- ...

  8. 分页插件jquery.simplePagination.js使用

    利用ecshop后台,利用插件更改分页显示样式遇到的问题,由于是利用Ajax获取数据进行页面数据更新?所以出现了以下情况: 初始化页面前 : 分页更新后: 点击后出现了分页插件内容消失, 原因:分页一 ...

  9. MongoDB 复制集模式Replica Sets

    1.概述 复制集是一个带有故障转移的主从集群.是从现有的主从模式演变而来,增加了自动故障转移和节点成员自动恢复. 复制集模式中没有固定的主结点,在启动后,多个服务节点间将自动选举 产生一个主结点.该主 ...

  10. JQuery树形目录制作

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DT ...