python gil_Python GIL(Global Interpreter Lock)
一、GIL介紹
GIL本質(zhì)就是一把互斥鎖,既然是互斥鎖,所有互斥鎖的本質(zhì)都一樣,都是將并發(fā)運(yùn)行變成串行,以此來(lái)控制同一時(shí)間內(nèi)共享數(shù)據(jù)只能被一個(gè)任務(wù)所修改,進(jìn)而保證數(shù)據(jù)安全。
可以肯定的一點(diǎn)是:保護(hù)不同的數(shù)據(jù)的安全,就應(yīng)該加不同的鎖。
要了解GIL,首先確定一點(diǎn):每次執(zhí)行python程序,都會(huì)產(chǎn)生一個(gè)獨(dú)立的進(jìn)程。例如python test.py,python aaa.py,python bbb.py會(huì)產(chǎn)生3個(gè)不同的python進(jìn)程
在一個(gè)python的進(jìn)程內(nèi),不僅有test.py的主線程或者由該主線程開(kāi)啟的其他線程,還有解釋器開(kāi)啟的垃圾回收等解釋器級(jí)別的線程,總之,所有線程都運(yùn)行在這一個(gè)進(jìn)程內(nèi)。
1、所有數(shù)據(jù)都是共享的
其中代碼作為一種數(shù)據(jù)也是被所有線程共享的(test.py的所有代碼以及Cpython解釋器的所有代碼)
2、所有線程的任務(wù),都需要將任務(wù)的代碼當(dāng)做參數(shù)傳給解釋器的代碼去執(zhí)行,即所有的線程要想運(yùn)行自己的任務(wù),首先需要解決的是能夠訪問(wèn)到解釋器的代碼
綜上:
如果多個(gè)線程的target=work,那么執(zhí)行流程是
多個(gè)線程先訪問(wèn)到解釋器的代碼,即拿到執(zhí)行權(quán)限,然后將target的代碼交給解釋器的代碼去執(zhí)行
解釋器的代碼是所有線程共享的,所以垃圾回收線程也可能訪問(wèn)到解釋器的代碼而去執(zhí)行,這就導(dǎo)致了一個(gè)問(wèn)題:
對(duì)于同一個(gè)數(shù)據(jù)100,可能線程1執(zhí)行x=100的同時(shí),而垃圾回收?qǐng)?zhí)行的是回收100的操作,解決這種問(wèn)題沒(méi)有什么高明的方法?
就是加鎖處理,如下圖的GIL,保證python解釋器同一時(shí)間只能執(zhí)行一個(gè)任務(wù)的代碼
二、GIL與Lock
GIL保護(hù)的是解釋器級(jí)的數(shù)據(jù),保護(hù)用戶自己的數(shù)據(jù)則需要自己加鎖處理,如下圖
三、GIL與多線程
有了GIL的存在,同一時(shí)刻同一進(jìn)程中只有一個(gè)線程被執(zhí)行
進(jìn)程可以利用多核,但是開(kāi)銷大,而python的多線程開(kāi)銷小,但卻無(wú)法利用多核優(yōu)勢(shì),要解決這個(gè)問(wèn)題,我們需要在幾個(gè)點(diǎn)上達(dá)成一致:
1. cpu到底是用來(lái)做計(jì)算的,還是用來(lái)做I/O的?
2. 多cpu,意味著可以有多個(gè)核并行完成計(jì)算,所以多核提升的是計(jì)算性能
3. 每個(gè)cpu一旦遇到I/O阻塞,仍然需要等待,所以多核對(duì)I/O操作沒(méi)什么用處
一個(gè)工人相當(dāng)于cpu,此時(shí)計(jì)算相當(dāng)于工人在干活,I/O阻塞相當(dāng)于為工人干活提供所需原材料的過(guò)程,工人干活的過(guò)程中如果沒(méi)有原材料了,則工人干活的過(guò)程需要停止,直到等待原材料的到來(lái)。
如果你的工廠干的大多數(shù)任務(wù)都要有準(zhǔn)備原材料的過(guò)程(I/O密集型),那么你有再多的工人,意義也不大,還不如一個(gè)人,在等材料的過(guò)程中讓工人去干別的活,
反過(guò)來(lái)講,如果你的工廠原材料都齊全,那當(dāng)然是工人越多,效率越高
結(jié)論:
對(duì)計(jì)算來(lái)說(shuō),cpu越多越好,但是對(duì)于I/O來(lái)說(shuō),再多的cpu也沒(méi)用
當(dāng)然對(duì)運(yùn)行一個(gè)程序來(lái)說(shuō),隨著cpu的增多執(zhí)行效率肯定會(huì)有所提高(不管提高幅度多大,總會(huì)有所提高),這是因?yàn)橐粋€(gè)程序基本上不會(huì)是純計(jì)算或者純I/O,所以我們只能相對(duì)的去看一個(gè)程序到底是計(jì)算密集型還是I/O密集型,從而進(jìn)一步分析python的多線程到底有無(wú)用武之地。
假設(shè)一種情況:
現(xiàn)在有四個(gè)任務(wù)需要處理,處理方式是要有并發(fā)的效果,解決方案可以是:
方案一:開(kāi)啟四個(gè)進(jìn)程
方案二:一個(gè)進(jìn)程下,開(kāi)啟四個(gè)線程
單核情況下,分析結(jié)果:
如果四個(gè)任務(wù)是計(jì)算密集型,沒(méi)有多核來(lái)并行計(jì)算,方案一徒增了創(chuàng)建進(jìn)程的開(kāi)銷,方案二勝
如果四個(gè)任務(wù)是I/O密集型,方案一創(chuàng)建進(jìn)程的開(kāi)銷大,且進(jìn)程的切換速度遠(yuǎn)不如線程,方案二勝
多核情況下,分析結(jié)果:
如果四個(gè)任務(wù)是計(jì)算密集型,多核意味著并行計(jì)算,在python中一個(gè)進(jìn)程中同一時(shí)刻只有一個(gè)線程執(zhí)行用不上多核,方案一勝
如果四個(gè)任務(wù)是I/O密集型,再多的核也解決不了I/O問(wèn)題,方案二勝
結(jié)論:
現(xiàn)在的計(jì)算機(jī)基本上都是多核,python對(duì)于計(jì)算密集型的任務(wù)開(kāi)多線程的效率并不能帶來(lái)多大性能上的提升,甚至不如串行(沒(méi)有大量切換)
但是,對(duì)于IO密集型的任務(wù)效率還是有顯著提升的。
四、多線程性能測(cè)試
1、計(jì)算密集型:多進(jìn)程效率高
from multiprocessing import Process
from threading import Thread
import os,time
def work():
res=0
for i in range(100000000):
res*=i
l=[]
print(os.cpu_count()) #本機(jī)為4核
start=time.time()
for i in range(4):
# p=Process(target=work) #耗時(shí)18s多
p=Thread(target=work) #耗時(shí)26s多
l.append(p)
p.start()
for p in l:
p.join()
stop=time.time()
print('run time is %s' %(stop-start))
2、i/o密集型:多線程效率高
from multiprocessing import Process
from threading import Thread
import threading
import os,time
def work():
time.sleep(2)
print('===>')
l=[]
print(os.cpu_count()) #本機(jī)為4核
start=time.time()
for i in range(400):
p=Process(target=work) #耗時(shí)4s多,大部分時(shí)間耗費(fèi)在創(chuàng)建進(jìn)程上
# p=Thread(target=work) #耗時(shí)2s多
l.append(p)
p.start()
for p in l:
p.join()
stop=time.time()
print('run time is %s' %(stop-start))
應(yīng)用:
多線程用于IO密集型,如socket,爬蟲(chóng),web
多進(jìn)程用于計(jì)算密集型,如金融分析
總結(jié)
以上是生活随笔為你收集整理的python gil_Python GIL(Global Interpreter Lock)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: jenkins 下载插件 一直失败_Je
- 下一篇: python线性回归实例_sklearn