GIL全局解释器锁
GIL全局解释器锁
这个GIL并不是python的特性,他是只在Cpython解释器里引入的一个概念,而在其他的语言编写的解释器里就没有这个GIL例如:Jython,Pypy
GIL 本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是讲并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
GIL存在的原因
CPython在执行多线程的时候并不是线程安全的,所以为了程序的稳定性,加一把全局解释锁,能够确保任何时候都只有一个Python线程执行。
GIL的弊端
GIL对计算密集型的程序会产生影响。因为计算密集型的程序,需要占用系统资源。GIL的存在,相当于始终在进行单线程运算,这样自然就慢了。
IO密集型影响不大的原因在于,IO,input/output,这两个词就表明程序的瓶颈在于输入所耗费的时间,线程大部分时间在等待,所以它们是多个一起等(多线程)还是单个等(单线程)无所谓的。
这就好比,你在公交站等公交时,你们排队等公交(单线程)还是沿着马路一字排开等(多线程)是无所谓的。公交车(即input,即输入的资源)没来,哪种方式都是瞎折腾。
# 1. cpu到底是用来做计算的,还是用来做I/O的?
# 2. 多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能
# 3. 每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处
# 分析:
我们有四个任务需要处理,处理方式肯定是要玩出并发的效果,解决方案可以是:
方案一:开启四个进程
方案二:一个进程下,开启四个线程
# 单核情况下,分析结果:
如果四个任务是计算密集型,没有多核来并行计算,方案一徒增了创建进程的开销,方案二胜
如果四个任务是I/O密集型,方案一创建进程的开销大,且进程的切换速度远不如线程,方案二胜
# 多核情况下,分析结果:
如果四个任务是计算密集型,多核意味着并行计算,在python中一个进程中同一时刻只有一个线程执行用不上多核,方案一胜
如果四个任务是I/O密集型,再多的核也解决不了I/O问题,方案二胜
# 结论:现在的计算机基本上都是多核,python对于计算密集型的任务开多线程的效率并不能带来多大性能上的提升,甚至不如串行(没有大量切换),但是,对于IO密集型的任务效率还是有显著提升的。
- 多线程性能测试
# 计算密集型:多进程效率高
from multiprocessing import Process
from threading import Thread
import os, time
def work():
res = 0
for i in range(100000000):
res *= i
if __name__ == '__main__':
l = []
print(os.cpu_count()) # 本机核数:4
start_time = time.time()
for i in range(4):
p = Process(target=work) # 运行时间: 16.431584358215332
# p = Thread(target=work) # 运行时间: 31.87273621559143
l.append(p)
p.start()
for p in l:
p.join()
end_time = time.time()
print('运行时间: %s'%(end_time - start_time))
# I/O 密集型:多线程效率高
from multiprocessing import Process
from threading import Thread
import os, time
def work():
time.sleep(1)
print('===>')
if __name__ == '__main__':
l = []
print(os.cpu_count()) # 本机核数:4
start_time = time.time()
for i in range(400):
# p = Process(target=work) # 运行时间:25.596331119537354
p = Thread(target=work) # 运行时间:1.0874786376953125
l.append(p)
p.start()
for p in l:
p.join()
end_time = time.time()
print('运行时间:%s'%(end_time - start_time))
小结:
多线程用于IO密集型
多进程用于计算密集型
千里之行,始于足下。