GIL
CPython
在解释器进程级别有一把锁,叫做GIL(Global Interpreter Lock)
,即全局解释器锁。
GIL
保证CPython
进程中,只有一个线程执行字节码。甚至是在多核CPU
的情况下,也只允许同时只有一个CPU
上运行该进程的一个线程。
CPython
中:
IO
密集型,某个线程阻塞,就会调度其他就绪线程CPU
密集型,当前线程可能会连续的获得GIL
,导致其它线程几乎无法使用CPU
在CPython
中由于有GIL
的存在,IO
密集型,使用多线程较为合理;CPU
密集型,使用多进程,要绕开GIL
。
新版Python
正在努力优化GIL
的问题,但不是移除。移除GIL
,会将此CPython
单线程的执行效率。
Python
中绝大多数的内置数据结构的读写操作都是原子操作。由于GIL
的操作,Python
的内置数据类型在多线程编程时就变成安全的了,但是实际上他们本身并不是线程安全类型。
如果在意GIL
,可使用erlang Go
等语言。
CPU
密集型查看下面两个示例,一个主线程单独运算和四个线程分别运算,时间竟然相差无几,验证了GIL
锁的存在。
CPU
密集型的运算,适合使用多进程。
import logging
import datetime
FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)
start = datetime.datetime.now()
def clac(n: int=1000000000):
sum = 0
for _ in range(n):
sum += 1
for i in range(4):
clac()
delta = (datetime.datetime.now() - start).total_seconds()
logging.info(delta)
# 2022-04-21 10:40:51,945 MainThread 50912 107.745666
import logging
import datetime
import threading
FORMAT = "%(asctime)s %(threadName)10s %(thread)8d %(message)s"
logging.basicConfig(level=logging.INFO, format=FORMAT)
start = datetime.datetime.now()
def clac(n: int=1000000000):
sum = 0
for _ in range(n):
sum += 1
logging.info(sum)
return sum
for i in range(4):
t = threading.Thread(target=clac, name='clac-{}'.format(i + 1))
t.start()
t.join()
delta = (datetime.datetime.now() - start).total_seconds()
logging.info(delta)
# 2022-04-21 10:44:42,513 clac-1 41712 1000000000
# 2022-04-21 10:45:09,500 clac-2 41900 1000000000
# 2022-04-21 10:45:36,390 clac-3 46436 1000000000
# 2022-04-21 10:46:02,827 clac-4 908 1000000000
# 2022-04-21 10:46:02,827 MainThread 10584 107.654591