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.)
首先明确一点:GIL不是Python的特性,它是Cpython解释器引入的一个概念.
一.什么是GIL(全局解释器锁)
GIL本质上也是一个互斥锁,那么它的原理与互斥锁一样,都是让并发线程在同一时刻只能有一个执行.即统一进程下的多个线程在同一时刻只有一个执行.那么在CPython解释器中一个进程中的多个线程无法实现并行,意味着失去了多核优势,但不影响并发的实现.
二.为何要有GIL
因为CPython解释器自带的垃圾回收机制不是线程安全的.那么这句话是什么意思呢?
首先同一个进程内所有数据是该进程下所有线程共享的(包括垃圾回收线程),而所有线程想要执行自己的任务,需要先访问解释器的代码,而解释器代码是所有线程共享的,那么这就导致了一个问题:
如果没有GIL的话,那么垃圾回收线程与其他线程同时运行,对于同一个共享数据进行修改时,可能线程1马上执行这个数据时,而垃圾回收线程也执行回收的操作.
其实解决这个问题很简单,就是加锁处理,就是GIL,它保证了cpython解释器同一时间只能执行一个任务的代码,控制了同一个时刻共享数据只能被一个任务修改.保证内存管理的数据是安全的.而保护用户自己额数据则需要自己加锁处理.
GIL可以比喻成执行权限,同一进程下的多个线程想要执行必须首先拿到这个执行权限.
四.GIL vs 自定义锁
GIL相当于执行权限,会在任务无法执行(遇到IO操作时),有优先级更高的情况时,会被强行释放.
自定义锁是需要自己释放,即使遇到了无法执行的情况.
五.多进程与多线程
多进程意味着可以并行,有着多核优势,在计算密集型的情况下占有优势.
多线程意味着统一进程下多个线程并发执行,在IO密集型的情况占有优势(此时有多核没有什么作用,因为CPU是计算的)
六.python多线程是不是比较鸡肋?
多线程意味着统一进程下多个线程都是并发执行的,不能实现并行,那么意味着它失去了多核优势.
什么是多核优势,多核优势就是在计算密集型的情况或者说纯计算的情况下,多核或者说多个CPU并行执行占有优势,可以提高效率.那么我们也可以用多进程的方式去解决.
但是在我们工作中遇到的大多数都是IO密集型的情况,此时多核并不能带来优势,还不如一个核在多个任务中来回切并发执行,这就是多线程的用处.当然也可以使用多进程的方式,每个进程下一个任务,但是创建进程的开销比较大而且切换的时间比线程之间切换的时间花费多.
计算密集型
def work():
res = 0
for i in range(100000000):
res *= i
if __name__ == '__main__':
l = []
print(os.cpu_count())
start = time.time()
for i in range(4):
p = Process(target=work)
# p=Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
stop = time.time()
print('run time is %s' % (stop - start))
IO密集型
def work():
time.sleep(2)
print('===>')
if __name__ == '__main__':
l=[]
print(os.cpu_count())
start=time.time()
for i in range(300):
# p=Process(target=work)
p=Thread(target=work)
l.append(p)
p.start()
for p in l:
p.join()
stop=time.time()
print('run time is %s' %(stop-start))