既然有了GIL锁,CPython还要多线程干什么?
''' GIL:全局解释器锁的来历 四核:同一时刻真正有四个任务在运行,多核的意义在于此 单核:看上去是并发的,因为进行了上下文切换,单核永远是串行的,并发是假象 CPython中不论有多少核,只有一个核心的CPU资源被使用,因为同一时间只有一个线程能拿到GIL锁。 为什么这样设计呢?CPython的线程是调用操作系统的c语言的原生线程,该线程不是线程安全的。balabalabala... ===================官方解释====================== 在CPython中,全局解释器锁或GIL是一个互斥锁, 这个锁是必要的,主要是因为CPython的内存管理不是线程安全的。 (然而,由于GIL的存在,其他特征已经发展成依赖于这个锁形成的线程安全环境) ================================================= 由于GIL是全局的,所以在多线程(多核)时,线程需满足两个条件才能执行 1、被操作系统调度出来【操作系统允许它占用CPU】 2、获取到GIL锁【CPython解释器允许它执行指令】 怎样避免GIL影响性能?就是减少GIL锁对线程的影响 1、在单核机器上Python和其他语言一样,“多线程”只是是假象实际上还是单线程。第2条总能满足,性能没有影响 2、在多核机器上Python只有一个核心在使用。因为多核多线程中,同时只有一个线程申请到GIL。所以要尽量减少 对GIL的申请,例如:全IO密集型任务时才使用多线程, 避免在计算密集型任务中使用Python的多线程(因为计算需要CPU资源,会频繁申请GIL锁,造成其他线程等待) ================================================ 用户程序锁: 还有一个锁,用户程序锁,跟GIL没有关系。这个锁是保证某个全局变量的修改必须是串行的 假设有全局变量num=0,线程作用是对num执行加1的操作。 在线程调度的时候,A线程获取到num=0,A执行到一半,CPU执行B线程, B线程将num修改num=1,CPU继续执行A线程,A线程执行完毕,将num修改成1,而num在AB线程执行完其实应该为2 此时就应该对num变量加锁。这个锁就是用户程序锁,这个锁不同于GIL锁。GIL锁是同一时间只能有一个线程使用 CPU资源,要保证同一时间只有一个线程访问某块数据就需要“用户程序锁” ===================================================== 在Python2.x版本+某些操作系统里不加用户程序锁会导致结果不准。 在Python3.x版本+某些操作系统里不确定是否有同样的问题,但是最好加上。 ''' import threading import time lock = threading.Lock() class Foo: num = 0 @classmethod def run(cls): time.sleep(2) lock.acquire() cls.num += 1 lock.release() thread_objs = [] for i in range(20): thread_objs.append(threading.Thread(target=Foo.run, args=())) start_time = time.time() for i in thread_objs: i.start() for i in thread_objs: i.join() print(Foo.num) # 20 print(time.time()-start_time) # 2.0051145553588867