3.2.6 Python的GIL锁内部机制
GIL(Global Interpreter Lock)全局解释器锁
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
在Cpython中,全局解释器锁,或者叫GIL,是一个互斥体,它可以阻止多个来自执行中的Python字节码的本地线程同时运行。这个锁很重要主要是因为CPython的内存管理在线程上不安全。(不管怎样,由于GIL的存在,其他特性已经越来越依赖于它(指GIL)强制实行的保证)
大概意思就是,python的多线程是假多线程,因为有GIL的存在,同一时间CPU(不管你几个核)只能执行一条线程。这个GIL已经不可能去掉了,因为它已经成为了CPython的基石的一部分,CPython其他很多功能都依赖它才能运行。
上面是英文老师说的,说必须依赖;但下面中文老师又说了,说可以完全不依赖。
首先需要明确的一点是GIL
并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL
归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL。
这里不争论,先继续往下走。
这篇文章透彻的剖析了GIL对python多线程的影响,强烈推荐看一下:http://www.dabeaz.com/python/UnderstandingGIL.pdf
下面这个示例很好的解释了GIL
import threading import time start_time = time.time() def run(n): global num time.sleep(0.8) num += 1 num = 0 t_l = [] #存放线程 for i in range(10000): t = threading.Thread(target=run, args=(‘t-%s‘ % i, )) t.start() t_l.append(t) #为了不阻塞其他线程的启动,先放在列表里,最后join for t in t_l: #循环线程列表,执行所有线程 t.join() print(‘All threads have ran down!‘, threading.current_thread(), threading.activeCount()) time.sleep(0) #current_thread显示当前线程 #activeCount显示活动的线程数 print(‘Num: ‘, num) print(‘Total Cost: ‘, time.time() - start_time)
运行结果
All threads have ran down! <_MainThread(MainThread, started 2848)> 1 Num: 10000 Total Cost: 2.0331716537475586
windows下计算正确,但在Ubuntu Linux或MAC OS上可能会出现计算错误的情况。所以,为什么会计算错误呢?看下图
- 全局变量num经过图中前5步的执行,到第5步(由于CPU切换上下文所设定的时间)GIL被强制释放。但此时并未完成num+1,所以num此时还是0。
- 6-11步调用num,完成num+1,并赋值给num,释放GIL,此时num=1。
- 11-13步调用num,重复之前1-4接13步,这次完成num+1运算,num=2。
亲,我这笔记写的这么好,你看懂了吗?