当前位置: 首页 > 后端技术 > PHP

Python多线程鸡肋?

时间:2023-03-29 21:18:57 PHP

为什么有人说Python多线程鸡肋?知乎上有人提出了这样的问题。在我们的常识中,多进程、多线程就是以并发的方式充分利用硬件资源,提高程序的运行效率。怎么在Python里变得鸡肋了?可能有同学知道答案,因为Python中臭名昭著的GIL,什么是GIL?为什么会有GIL?多线程真的鸡肋吗?可以删除GIL吗?带着这些疑问,我们一起往下看,同时也需要你有一点耐心。多线程就没用了吗?我们先做一个实验。实验非常简单。就是递减一个数“1亿”,递减到0时程序就会终止。如果我们用单线程来执行这个任务,完成时间是多少?使用多线程会花费多少?给我看代码#taskdefdecrement(n):whilen>0:n-=1singlethreadimporttimestart=time.time()decrement(100000000)cost=time.time()-start>>>6.541690826416016inmy在4核CPU的电脑上,单线程耗时为6.5秒。有人可能会问,线程在哪里?事实上,任何程序在运行时,默认都会有一个主线程在执行。(这里就不展开线程和进程了,我会单独写一篇文章)=decrement,args=[50000000])t1.start()#启动线程并执行任务t2.start()#同上t1.join()#主线程阻塞,直到t1执行完毕,主线程继续执行t2。join()#同上cost=time.time()-start>>>6.85541033744812创建两个子线程t1和t2,每个线程进行5000万次减法运算,两个线程都执行完后,主线程终止程序运行.结果,两个线程协同执行的时间为6.8秒,比较慢。按理说两个线程同时在两个CPU上并行运行,时间应该减半,现在不减反增。是什么导致多线程不愉快和缓慢?原因在于GIL。在Cpython解释器(Python语言的主流解释器)中,有一个全局解释器锁(GlobalInterpreterLock)。解释器在解释执行Python代码时,首先要获得这把锁,这意味着任何一个线程在同一时刻都可能正在执行代码。其他线程要想获得CPU执行代码指令,必须先获得锁。如果锁被其他线程占用,那么该线程只能等待,直到拥有锁的线程释放锁后,才有可能执行代码指令。所以,这也是为什么两个线程一起执行比较慢的原因,因为同一时间只有一个线程在运行,其他线程只能等待。即使是多核CPU,也没有办法让多个线程同时“并行”。执行代码只能交替执行,因为多线程涉及在线文本切换和锁机制处理(获取锁、释放锁等),所以多线程执行慢而不快。GIL什么时候发布?当线程遇到I/O任务时,释放GIL。当一个计算密集型(CPU-bound)的线程执行了100个解释器ticks(ticks可以粗略地认为是Python虚拟机的指令)时,GIL也会被释放。可以通过sys.setcheckinterval()设置步长,通过sys.getcheckinterval()检查步长。与单线程相比,这些可能是多线程带来的额外开销。为什么CPython解释器要这样设计?多线程是适应现代计算机硬件飞速发展,充分利用多核处理器的产物。通过多线程,可以高效地利用CPU资源。Python诞生于1991年,那时候的硬件配置远没有今天豪华。现在一台普通的服务器有32核64G内存已经不常见了,但是多线程就有问题了。如何解决共享数据的同步和一致性问题,因为当多个线程访问共享数据时,两个线程可能会同时修改一个数据,在这种情况下,如果没有合适的机制来保证数据的一致性,程序最终会导致一个例外。因此,Python之父创建了全局线程锁。不管你的数据有没有同步问题,反正一刀切。数据安全。这就是多线程鸡肋的原因,因为它没有细粒度的控制数据安全,而是用一种简单粗暴的方式来解决。在20世纪90年代,这种解决方案不是问题。毕竟那时候的硬件配置还很简单,单核CPU还是主流,多线程的应用场景并不多。大多数时候,它仍然是单线程的方式。运行时,单线程不涉及线程上下文切换,效率高于多线程(在多核环境下,不适用此规则)。所以,用GIL的方式来保证数据的一致性和安全性,也不一定不能接受,至少在当时是一种成本非常低的实现方式。那么去掉GIL是否可行呢?真的有人做了这么多,结果却令人失望。1999年,GregStein和MarkHammond这两个伙伴创建了一个Python分支,去掉了GIL,在所有可变数据结构上用更细粒度的GIL代替了GIL。锁。但是经过benchmark测试,没有GIL的Python在单线程情况下执行效率慢了近2倍。Python之父说:基于以上考虑,在不花费太多精力的情况下去掉GIL并没有多大价值。总结CPython解释器提供了GIL(GlobalInterpreterLock)来保证线程数据同步,那么有了GIL,我们还需要线程同步吗?多线程在IO密集型任务中表现如何?欢迎大家留言,离开前请点赞~感谢阅读。