11-1 Python中的GIL

GIL: global interpreter lock (cpython)
GIL控制的字节码的执行,锁控制的是Python代码

  1. 什么是字节码,怎么查看字节码?
#通过dis模块查看函数add的字节码
import dis
def add(a):
    a = a+1
    return a
print(dis.dis(add))

#运行结果
  6           0 LOAD_FAST                0 (a)
              2 LOAD_CONST               1 (1)
              4 BINARY_ADD
              6 STORE_FAST               0 (a)

  7           8 LOAD_FAST                0 (a)
             10 RETURN_VALUE

2.GIL作用
GIL使得同一时刻只有一个线程在一个cpu上执行字节码。保证字节码的执行是线程安全的

有了GIL是不是就是多线程安全的,不需要考虑线程间同步呢?
答案肯定不是的,因为GIL会在适当的时候释放的。举例说明

total=0
def add():
    #1. dosomething1
    #2. io操作
    #3. dosomething3
    global total
    for i in range(1000000):
        total +=1

def desc():
    global total
    for i in range(1000000):
        total -=1

import threading
thread1=threading.Thread(target=add)
thread2=threading.Thread(target=desc)
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print(total)

#运行结果:每次结果都不一样

不是说有GIL吗? 为什么还会出现这种现象呢?
首先,了解GIL的知识点:

  • 对于有I/O操作的多线程,每次遇到I/O操作便会进行GIL锁的释放
  • 如果是纯计算的程序,没有I/O操作,解释器会根据sys.setcheckinterval的设置来自动进行线程间的切换,默认情况下每隔100个时钟(python的内部时钟,对应于解释器执行的指令)就会释放GIL锁从而轮换到其他线程的执行
以简单实例说明
# a是全部共享变量
def add1(a):
    a += 1

def desc1(a):
    a -= 1
import dis
print(dis.dis(add1))
print(dis.dis(desc1))

#运行结果
 51           0 LOAD_FAST                0 (a)
              2 LOAD_CONST               1 (1)
              4 INPLACE_ADD
              6 STORE_FAST               0 (a)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

 55           0 LOAD_FAST                0 (a)
              2 LOAD_CONST               1 (1)
              4 INPLACE_SUBTRACT
              6 STORE_FAST               0 (a)
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE

# 使用伪代码来说明,考虑一种极端情况来解释
add1
"""
1. load a            # a=0, 切换线程到desc1 ①
2. load 1            # 1  ③
3. +                 # 1  ⑤
4. 赋值给a            # a=1 ⑦
"""

desc1
"""
1. load a           # a=0, 切换为add1   ②
2. load 1           # 1 ④
3. -                # -1 ⑥
4. 赋值给a           # a=-1 ⑧
"""

经过add1和desc1操作之后,a要不就是1或者-1.而不是结果0

总结:因为线程同步解决的是代码的线程安全性问题,而GIL解决的只是字节码的线程安全,概念不一样

猜你喜欢

转载自blog.csdn.net/shfscut/article/details/80438584