GIL:global interpreter lock(cpython)
在python中,一个线程对应于c中的一个线程。
gil使得同一个时刻只有一个线程在CPU上执行字节码(python在执行的时候会将py文件编译成字节码)。
同时也预示着无法将多个线程映射到多个CPU上运行,无法体现多CPU的优势。
只要运行一个python进程,里面不管有多少个线程,他都只能运行在一个CPU上。
在其它静态语言里面可以将多个线程映射到多个CPU上。
因此,在python里面,并发就非常受限,python一直在努力的去GIL化。
但是短期内是无法实现的,因为大量的第三方包都是使用cpython来完成的。
当然也有其它的解释器,比如pypi去GIL化。
GIL锁为了线程运行安全,因为多个线程运行,尤其是运行同一段代码的时候,十分容易出错。
所以python在最初的时候就加了一把GIL锁,这使得同一时刻只有一个线程在CPU上执行字节码。
这样保证了某种程度上线程是安全的。python正是由于GIL锁使得多线程的效率不是很高。
虽然同一时刻只有一个线程运行在CPU上,那么是否就意味着编写多线程编码就是安全的了?
就不去考虑线程间的同步了?实际上不是的。
通过下面示例可以知道,GIL锁会在适当的时间释放掉,不会一直占有。
from threading import Thread from multiprocessing import Process a = 0 def add(): global a b = 0 for i in range(1000000): a += 1 b += 1 print("循环次数:",b) def desc(): global a c = 0 for i in range(1000000): a -= 1 c += 1 print("循环次数:",c) def task(): t1 = Thread(target=add) t2 = Thread(target=desc) t1.start() t2.start() t1.join() t2.join() print(a) if __name__ == "__main__": for i in range(4): p = Process(target=task) p.start() p.join()
如果按照代码的逻辑,那么最终打印的结果都为0。
执行结果:
循环次数: 1000000
循环次数: 1000000
-4652
循环次数: 1000000
循环次数: 1000000
468960
循环次数: 1000000
循环次数: 1000000
-61354
循环次数: 1000000
循环次数: 1000000
196468
我们发现每次的执行结果都不相同,
原因可能有二:
第一,两个任务没有做完,
第二,两个任务之间的变量相互影响,也就是GIL锁在途中被释放掉了。
我们通过打印循环次数说明任务都做完了,那么就只能是GIL锁在任务执行过程中被释放掉了,导致两个任务的a变量相互影响。
当把GIL这把锁交给一个线程之后,他不会等到线程执行完毕之后再释放。
它会根据字节码执行的行数以及时间片,然后把它释放出来。
另外,当GIL遇到IO操作的时候也会释放。也正是由于这个原因,GIL在IO操作频繁的时候非常适用。