zoukankan      html  css  js  c++  java
  • [Python 多线程] GIL全局解释器锁 (十三)

    Queue

    标准库queue模块,提供FIFO(先进先出)的Queue、LIFO(后进先出)的队列、优先队列。

    Queue类是线程安全的,适用于多线程间安全的交换数据。内部使用了Lock和Condition。

    使用魔术方法,实现的容器的大小,是不准确的。not reliable!

    因为在多线程中,如果不加锁,是不可能获得准确的大小的,因为当刚刚读取到一个大小数值,还没有取走,就有可能被其它线程修改了。

    Queue类的size虽然加了锁,但是,依然不能保证立即get、put就能成功,因为读取队列大小qsize和get、put方法是分开的。

    全局解释器锁:

    CPython在解释器级别的一把锁,叫GIL全局解释器锁。(Ruby也有)

    Global Interpreter Lock

    程序编译成字节码,程序想跑多线程,但是GIL保证CPython进程中,同一时刻只能有一个线程执行字节码。

    所以,哪怕是在多CPU的情况下,即使每个线程恰好调度到了每个CPU上,有了这把大锁,同时只能有一个CPU使用CPython执行一个线程的字节码,其它线程只能阻塞等待。

    也就是说只要有这把锁,CPython中根本就没有真正的多线程。同一时刻只有CPU的一个线程被调度使用。

    GIL锁从CPython最初版本到现在一直存在。

     CPython中:
    IO密集型,由于线程阻塞,就会调度其它线程;(wait会让出时间片,让其它线程有机会被调度、sleep不会),大量使用网络IO、磁盘IO

    CPU密集型,当前线程可能会连续的获得GIL,导致其它线程几乎无法使用CPU。(刚释放锁就又夺走了),大量占用CPU计算得数

    IO密集型,使用多线程;CPU密集型,使用多进程,绕开GIL。

    多进程时,同样每个进程内的线程也受GIL这把大锁的限制。

    新版CPython正在努力优化GIL的问题,但不是移除。

    如果非要使用多线程的效率问题,请绕行,选择其它语言erlang、Go等。

    保留GIL的原因:

    独裁者Guido坚持简单哲学(import this),对于初学者门槛低,不需要高深的系统只是也能安全、简单的使用Python。

    而且移除GIL,会降低CPython单线程的执行效率。

    模拟CPU密集型:

    #模拟CPU密集型
    import threading,logging,time,random,datetime
    DATEFMT="%H:%M:%S"
    FORMAT = "[%(asctime)s]	 [%(threadName)s,%(thread)d] %(message)s"
    logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT)
    
    
    def calc():
        sum = 0
        for _ in range(100000000):
            sum += 1
    
    start =datetime.datetime.now()
    
    calc()
    calc()
    calc()
    calc()
    calc()
    
    delta = (datetime.datetime.now() -start).tota
    
    运行结果:
    38.820701
    

      上例是单线程串行执行结果。

    对上例使用多线程:

    #模拟CPU密集型 多线程
    import threading,logging,time,random,datetime
    DATEFMT="%H:%M:%S"
    FORMAT = "[%(asctime)s]	 [%(threadName)s,%(thread)d] %(message)s"
    logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT)
    
    
    def calc():
        sum = 0
        for _ in range(100000000):
            sum += 1
    
    start =datetime.datetime.now()
    
    lst = []
    
    for _ in range(5):
        t = threading.Thread(target=calc)
        t.start()
        lst.append(t)
    
    for t in lst:
        t.join()
    
    delta = (datetime.datetime.now() -start).total_seconds()
    print(delta)
    
    运行结果:
    38.782773
    

      上面分别使用了单线程和多线程来测试效率,耗时都在38秒左右,所以因为GIL的存在,CPython中多线程根本没有任何优势,和单线程执行效率相当。

  • 相关阅读:
    报表设计器的使用之一:入门
    统计图开发之二:点图元
    统计图开发之一:画法定义
    集算器之五:序表
    集算器之四:程序流程
    忏悔录
    请不要离我而去
    所想和所做 所梦和所成
    做出改变,不断改变。
    Linux 操作命令
  • 原文地址:https://www.cnblogs.com/i-honey/p/8079384.html
Copyright © 2011-2022 走看看