一、什么是GIL
GIL 是python的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行。
我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有。
二、什么时候释放GIL锁
1、某个线程运行完后其他线程才能运行。
2、如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。
三、互斥锁(同步锁)和GIL的区别
1、举例:多线程执行任务造成数据混乱的问题
import threading
g_num=0
def run():
global g_num
for i in range(1000000):
g_num+=1
print(f'当前线程{threading.current_thread().name}的执行结果:',g_num)
if __name__ == '__main__':
thread=[]
for i in range(10):
t=threading.Thread(target=run)
t.start()
thread.append(t)
for t in thread:
t.join()
print('主线程执行完毕')
执行结果:很明显数据已经混乱了
当前线程Thread-1的执行结果: 1000000
当前线程Thread-2的执行结果:当前线程Thread-3的执行结果: 1581377
当前线程Thread-4的执行结果:2690126
当前线程Thread-5的执行结果: 35138763690126
当前线程Thread-6的执行结果: 当前线程Thread-7的执行结果: 5545936
4690126
当前线程Thread-8的执行结果:当前线程Thread-9的执行结果: 6244936
当前线程Thread-10的执行结果: 82094017209401
主线程执行完毕
Process finished with exit code 0
2、数据混乱的原因:
cpu分成多个时间片段,启动10线程,分配10个cpu时间片段,当我累加数字设置比较小的时候,在单个cpu时间片段内,for循环代码就执行完,就不会产生数据混乱的。当我数据设置的比较大时,在单个cpu时间片段内,for循环代码就执行不完,并且没有分配2个或2个以上的连续的cpu时间片段,导致一个cpu时间片段没有执行完该线程,下一个线程开始执行了
问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为"线程不安全”"。
3、解决数据混乱(同步锁、互斥锁)
lock=Lock():创建同步锁
lock.acquire():获得这把锁的钥匙
lock.release():释放锁;如果不释放锁,其他的线程拿不到锁,是不会运行的;
释放锁后其他线程会抢这把锁。谁先抢到这把锁不一定
必须使用同一把锁
import threading
g_num=0
def run():
global g_num
lock.acquire()
for i in range(1000000):
g_num+=1
print(f'当前线程{threading.current_thread().name}的执行结果:',g_num)
lock.release()
if __name__ == '__main__':
lock=threading.Lock()
thread=[]
for i in range(10):
t=threading.Thread(target=run)
t.start()
thread.append(t)
for t in thread:
t.join()
print('主线程执行完毕')
执行结果:
当前线程Thread-1的执行结果: 1000000
当前线程Thread-2的执行结果: 2000000
当前线程Thread-3的执行结果: 3000000
当前线程Thread-4的执行结果: 4000000
当前线程Thread-5的执行结果: 5000000
当前线程Thread-6的执行结果: 6000000
当前线程Thread-7的执行结果: 7000000
当前线程Thread-8的执行结果: 8000000
当前线程Thread-9的执行结果: 9000000
当前线程Thread-10的执行结果: 10000000
主线程执行完毕
四、总结:
多线程编程时通过调用threading模块的Lock函数,来获取一把互斥锁。互斥锁就是对共享数据进行锁定,保证同一时刻只有一个线程操作数据,是数据级别的锁。
GIL锁是解释器级别的锁,保证同一时刻进程中只有一个线程拿到GIL锁,拥有执行权限。