理解这个其实需要懂一点操作系统的知识,不得不感叹学科之间其实很多都是有联系的。
其实多线程就比如泡面的时候一边烧开水一边放酱料,两不耽误。
在单核cpu中线程通过并发来实现cpu运算资源的高效使用
import time, threading
# 新线程执行的代码:
def loop():
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n = n + 1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)
在多核cpu中由于每个内核都能跑一个线程,所以n个内核的cpu就能并行(注意这里是并行了不再是并发了)n个线程
但是当python在多核cpu中却存在一定的性能缺陷,这里我们试着多开几个线程看看
import threading, multiprocessing
def loop():
x = 0
while True:
x = x ^ 1
if __name__=='__main__':
for i in range(multiprocessing.cpu_count()):
t = threading.Thread(target=loop)
t.start()
这里开了和cpu个数相等的线程,按道理来说1个线程的死循环可以跑满1个内核,所有内核都跑一个循环cpu利用率肯定会爆炸吧
但是结果呢?
这里就 涉及到了GIL(Global Interpreter Lock
)全局解释器锁在我查了各种资料后得出的结果就是,GIL是很久以前的
大佬为了防止线程崩溃设置的线程保护措施,简单来说 就是线程执行前先加一个全局锁,但这个锁只有1个,所以不管你
几个内核都只能同时跑1个线程这个锁不是python的特性,是解释器CPython的特性,像JPython就没有这个东西。
当时市面上还没有出现多核cpu,摩尔定律还没有失效,所以在当时这个东西还是很好用的,但随着技术进步这
个慢慢就成了python的累赘
来看解决办法,我了解到的方法是通过多进程来实现,试验了一下,确实有用
import threading, multiprocessing
from multiprocessing import Process
def loop():
x = 0
while True:
x = x ^ 1
if __name__=='__main__':
for i in range(multiprocessing.cpu_count()):
t = Process(target=loop)
t.start()
至于为什么多进程可以实现,我在stackoverflow上看到的解答是,python为每个进程配一个解释器,这样下来
n个进程就有n个解释器,每个进程一个主线程,GIL就加在这个主线程上,自然就突破了GIL的性能限制。
(至于更深入的原理可能需要等更加深入学习才能解释出来)
这里需要明确的是,虽然有GIL但是并不代表python的多线程就是鸡肋,这个其实把我坑了有段时间,我一度以为python的多线程没有用,但实际上多线程在解决IO密集型的任务上依旧有很大的优势,毕竟人家就是为这个而生的。