- GIL是Cpython解释器中使用的一种机制
- 当前运行的线程持有GIL,GIL保证解释器当前只有一个线程
- 只要一进行I/O操作就释放掉GIL
- 对于绑定CPU 并且从不进行I/O操作的线程当做特例对待
- 实质是互斥锁,即将并行变成串行
- 每次执行一个python脚本,就产生一个独立的进程,所有开的线程都运行在这一个进程内
- 所有线程共享进程数据,所有线程的任务都需要将任务的代码当做参数传给解释器的代码去执行。对于多个线程访问解释器的代码,加锁处理-GIL
GIL.acquire()
解释器的代码
GIL.release()
- GIL保护的是解释器级的数据,保护用户自己的数据则需要自己加锁处理。
- CPU是用来完成计算的,多CPU意味着可以有多个核并行完成计算,所以多核提升的是计算性能,每个CPU一旦遇到I/O,扔需要等待,所以CPU对I/0操作没用。
- 进程可以利用多核,但是开销大,线程开销小,可是无法利用多核的优势,因为同一个进程中只有一个线程被执行,GIL锁控制只有一个线程拿到Cpython代码。
- 总结:
1、 单核计算机无论是计算密集型还是I/O密集型都是开多线程合适
2、多核计算机:计算密集型开多进程,I/O密集型开多线程合适
3、计算密集型:eg 金融分析
I/O密集型:比如socket,爬虫和web - 多核下,性能测试,本机四核
对于计算密集型多进程
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('本机CPU核数%s'%(os.cpu_count()))
start=time.time()
for i in range(4):
p=Process(target=work) #开多进程
l.append(p)
p.start()
for i in l:
p.join()
stop=time.time()
print('mutiprocesss run time is %s' %(stop-start))
结果:
本机CPU核数4
mutiprocesss run time is 10.67342233657837
计算密集型多线程
from threading import Thread
import os,time
def work():
res=0
for i in range(100000000):
res*=i
if __name__=='__main__':
l=[]
print('本机CPU核数%s'%(os.cpu_count()))
start=time.time()
for i in range(4):
p=Thread(target=work) #开多线程
l.append(p)
p.start()
for i in l:
p.join()
stop=time.time()
print('mutiprocesss run time is %s' %(stop-start))
结果:
本机CPU核数4
mutiprocesss run time is 21.925345182418823
I/O密集型多进程
from multiprocessing import Process
#from threading import Thread
import os,time
def work():
time.sleep(2)
print('====>')
if __name__=='__main__':
l=[]
print('本机CPU核数%s'%(os.cpu_count()))
start=time.time()
for i in range(400):
p=Process(target=work) #开多进程
l.append(p)
p.start()
for i in l:
p.join()
stop=time.time()
print('mutiprocesss run time is %s' %(stop-start))
结果:
...
====>
====>
====>
====>
====>
====>
====>
====>
====>
====>
====>
====>
mutiprocesss run time is 36.55142903327942 #时间都浪费在创建进程了
I/O密集型多线程
from threading import Thread
import os,time
def work():
time.sleep(2)
print('====>')
if __name__=='__main__':
l=[]
print('本机CPU核数%s'%(os.cpu_count()))
start=time.time()
for i in range(400):
p=Thread(target=work) #开多进程
l.append(p)
p.start()
for i in l:
p.join()
stop=time.time()
print('mutiprocesss run time is %s' %(stop-start))
结果:
====>
====>
====>
====>
====>
====>
====>
====>
====>
====>
====>
====>
====>
mutiprocesss run time is 2.2711379528045654
====>
====>
思考:
I/O密集型的操作即使开多进程,四个进程也只能有四个线程在运行,因为有GIL锁的控制,每个CPU 中只有一个线程拿到了Cpython解释器的代码,所以开多进程优化效果不明显,有时不如不开多进程;I/0密集型的操作开多线程,因为线程开销小,可以实现多任务并发执行I/0任务。在执行I/0操作时,任务是都释放掉GIL锁的。计算密集型任务开多进程是在多核中并发,并且CPU是用来完成计算的,因此对于计算密集型任务使用多进程优化效果肯定比多线程(一个进程一个核)中好得多。