python GIL问题的解释(global interpreter lock)
早期cpython(python最主要的解释器)为了实现多线程的功能,提高CPU利用率,暴力无脑使用了GIL机制(全局锁)来解决线程之间数据同步问题。
也就是因为历史问题埋了大坑。
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的机制下,正常的cpu切换步骤中,在线程获得cpu时间片后,还需要获得GIL才能够执行。
在单核时代,这种机制是ok的,因为只有在GIL释放之后才会触发OS的线程调度,那么这时候其它线程势必能够获取GIL,实现线程的切换执行。
如果是多核心,那么就有问题了,因为只有一把大锁GIL,那么势必在任何时候,只能有一个线程在执行,也就是说只有一个cpu核心真正执行代码。
在多核架构下的线程调度,就会出现:虽然线程获得了cpu时间片,然而确没有得到GIL只好白白浪费CPU时间片,然后等待切换进入等待,等待下一次被唤醒,如此恶性循环。
现象:多线程的执行可能还不如单线程执行的时间开销
import threading import time def hehe(repeat): a=0 for _ in xrange(repeat): a += 1 print a t1=threading.Thread(target=hehe,args=(100000000,)) t2=threading.Thread(target=hehe,args=(100000000,)) start=time.time() print start t1.start() t1.join() t2.start() t2.join() end=time.time() print end print end-start # t1=threading.Thread(target=hehe,args=(100000000,)) # t2=threading.Thread(target=hehe,args=(100000000,)) # start=time.time() # print start # # t1.start() # t2.start() # t1.join() # t2.join() # # # end=time.time() # print end # print end-start
总结:
假如在多核计算机上进行IO密集型的计算,python的多线程确实可以提高效率;
如果线程中只要有一个是CPU密集型的线程,那么多线程效率可能会因为GIL反而下降
http://dabeaz.blogspot.com/2010/02/revisiting-thread-priorities-and-new.html
http://cenalulu.github.io/python/gil-in-python/