多线程编程(三) 线程和Python



全局解释锁

python代码的执行是由python虚拟机(又称解释器主循环)进行控制的。Python在设计时是这样考虑的,在主循环中同时只能有一个控制线程在执行,就像单核CPU中的多线程一样。内存中可以有许多程序,但是在任意给定时刻只能有一个程序在执行。同理,尽管python解释器中可以运行多个线程,但是在给定任意时刻只能有一个线程会被解释器执行。
对python虚拟机的访问是由全局解释器锁(GIL)控制的。这个锁就是用来保证同时,只能有一个线程运行的。

在多线程环境中。python虚拟机将按照下面所述的方式执行:

     1. 设置GIL

     2. 切换进一个线程取运行

     3 .执行下面操作之一

        a. 指定数量的字节码指令

        b.线程主动让出控制权(可以调用 time.sleep(0) 来完成)  

     4.把线程设置回睡眠状态(切换出线程)

     5.解锁GIL

     6.重复上述步骤

当调用外部代码(即,任意C/C++扩展内置函数)时,GIL会保持锁定,直至函数执行结束(因为在这期间没有 Python 字节码计数)。编写扩展函数的程序员有能力解锁 GIL,然而,作为Python开发者,你并不需要担心Python代码会在这些情况下被锁住。例如,对于任意面向 I/O 的 Python 例程 (调用了内置操作系统 C 代码的那种)。

GIL会在I/O 调用前被释放,以允许其他线程在 I/O 执行的时候运行。而对于那些没有太多 I/O操作的代码而言,更倾向于在该线程整个时间片内始终占有处理器和GIL 。换句话说,I/O 密集型的Python程序要比计算密集型的代码更好的利用多线程环境。

如果你对Python源代码、解释器主循环和GIL感兴趣,可以看看Python/ceval.c文件。

退出线程

当一个线程完成函数的执行时,它就会退出。另外,还可以通过诸如thread.exit()之类的退出函数,或者 sys.exit()之类的退出Python 进程的标准方法,或者抛出SystenExit异常,来使线程退出。不过,你不能直接 "终止"一个线程。

下一节将会讨论两个与线程有关的Python模块,不过在这两个模块中,不建议使用thread模块。给出这个建议有很多原因,其中最明显的一个原因是在主线程退出后,所有其它线程都会在没有被清理前的情况下直接退出。而另一个模块threading会确保在所有“重要的”子线程退出前,保持整个进程的存活。

而主线程应该做一个好的管理者,负责了解每个单独的线程执行什么,每个派生的线程需要哪些数据或参数,这些线程执行完成后会提供什么样的后果。这样,主线程就可以收集每个线程的结果,然后汇总成一个有意义的结果。

在Python中使用线程

python虽然支持多线程编程,但还是需要取决于它所运行的操作系统。
默认情况下,从源码构建的Python(2.0及以上的版本)或者Win32二进制安装的Python,线程支持是已经启用的。要确定你的解释器是否支持线程,只需要从Shell窗口中尝试导入thread模块即可,如下所示(如果线程是可用的,则不会产生错误)。

    >>>import thread
    >>>

如果你的Python 解释器没有将线程支持编译进去,模块导入将会失败。

     >>>import thread
     Traceback (innermost last):
           File "<stdin>", line 1, in?
     ImportError: No module named thread

这种情况下你需要重新编译你的Python解释器才能够使用线程。一般可以调用configure脚本的时候使用--with-thread选项。查阅你所使用的发行版本的README 文件,来获取如何在你的操作系统中编译线程支持的Python的指定命令。

不使用线程的情况

下面代码将使用 time.sleep() 函数来演示线程是如何工作的。`time.sleep()函数需要一个浮点型的参数,然后以这个给定的秒数进行“睡眠”,也就是说,程序的执行会暂时停止指定的时间。


from time import sleep,ctime

def loop0():
    print('start loop 0 at:', ctime())
    sleep(4)
    print('loop 0 done at:', ctime())

def loop1():
    print('start loop 1 at:', ctime())
    sleep(2)
    print('loop 1 done at:', ctime())

def main():
    print('starting at:',ctime())
    loop0()
    loop1()
    print('all Done at:', ctime())

if __name__ == '__main__':
    main()

创建两个时间循环:一个睡眠4秒(loop0());另一个睡眠2秒(loo1p())。如果在一个单进程或者单线程的程序中顺序执行loop0()loop1(),整个执行时间会达到6秒钟。而在启动loop0()loop1()以及执行其它代码时,也可能存在一秒钟的开销,使得整个时间达到7秒。

可以通过执行代码验证这一点,下面是输出结果。

starting at: Tue Jan 23 09:48:47 2018
start loop 0 at: Tue Jan 23 09:48:47 2018
loop 0 done at: Tue Jan 23 09:48:51 2018
start loop 1 at: Tue Jan 23 09:48:51 2018
loop 1 done at: Tue Jan 23 09:48:53 2018
all Done at: Tue Jan 23 09:48:53 2018

现在,假设loop0()loop1()中的操作不是睡眠,而是执行独立计算操作的函数,所有结果汇成一个最终结果。那么,让它们并行执行来减少总的执行时间是不是有用呢?这就是现在要介绍的多线程编程的前提。

Python的threading模块

Python提供了多个模块来支持多线程编程。包括,thread、threading、Queue模块等。程序是可以提供thread、threading模块来创建于管理线程。thread模块提供基本的线程和锁定支持;而threading模块提供更高级、功能更加全面的线程管理。使用Queue 模块,用户可以创建一个队列数据结构,用于在多线程之间进行共享。我们将分别查看这几个模块,并给出几个例子和中等规模的应用。

核心提示:避免使用thread模块
原因是threading模块更加先进,有更好的线程支持,并且thread模块中的一些属性和threading模块有冲突。另一个原因是低级别的thread模块拥有的同步原语很少(实际上只有一个),而threading模块则有很多。
不过处于对Python和线程学习的兴趣,我们将给出使用使用 thread 模块的一些代码,给出这些代码只是出于学习目的, 希望它能够更好的让你领悟为什么应该避免使用threading模块。我们还将展示如何使用更加合适的工具,如threading和Queue 模块中的那些方法。
避免使用thread模块的另一个原因是它对于进程何时退出没有控制,当主线程结束时,其他线程也都强制结束,不会发出警告或者进行适当的清理。如前所述,至少threading模块能确保重要的子线程在进程退出前结束。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容

  • 线程 引言&动机 考虑一下这个场景,我们有10000条数据需要处理,处理每条数据需要花费1秒,但读取数据只需要0....
    不浪漫的浪漫_ea03阅读 356评论 0 0
  • Python 面向对象Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对...
    顺毛阅读 4,205评论 4 16
  • 引言&动机 考虑一下这个场景,我们有10000条数据需要处理,处理每条数据需要花费1秒,但读取数据只需要0.1秒,...
    chen_000阅读 495评论 0 0
  • 前两年有个姑娘嫁给同村的啊哥,那两人一站、郎才女貌…… 爱美之心,人皆有之。那姑娘长得真的挺美的,特别是那...
    逆风追梦人阅读 151评论 2 4
  • 要说起如今的中国互联网,相信每个关注行业资讯的人都知道如今的互联网三巨头BAT,百度、阿里巴巴和腾讯。 2...
    由折衿阅读 246评论 0 1