成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

Python解析器Cpython的GIL解釋器鎖工作機(jī)制

89542767 / 429人閱讀

  小編寫(xiě)這篇文章的一個(gè)主要目的,主要是來(lái)給大家做個(gè)解答,解答的內(nèi)容主要是涉及到的內(nèi)容有Python解析器的一些相關(guān)介紹,介紹的內(nèi)容主要是Cpython的GIL解釋器解鎖相關(guān)機(jī)制的一些介紹。具體的內(nèi)容,下面就給大家詳細(xì)解答下。


  本節(jié)重點(diǎn)


  掌握Cpython的GIL解釋器鎖的工作機(jī)制


  掌握GIL與互斥鎖


  掌握Cpython下多線(xiàn)程與多進(jìn)程各自的應(yīng)用場(chǎng)景


  本節(jié)時(shí)長(zhǎng)需控制在45分鐘內(nèi)


  一引子


  定義:


  In CPython,the global interpreter lock,or GIL,is a mutex that prevents multiple


  native threads from executing Python bytecodes at once.This lock is necessary mainly


  because CPython’s memory management is not thread-safe.(However,since the GIL


  exists,other features have grown to depend on the guarantees that it enforces.)


  結(jié)論:在Cpython解釋器中,同一個(gè)進(jìn)程下開(kāi)啟的多線(xiàn)程,同一時(shí)刻只能有一個(gè)線(xiàn)程執(zhí)行,無(wú)法利用多核優(yōu)勢(shì)


  首先需要明確的一點(diǎn)是GIL并不是Python的特性,它是在實(shí)現(xiàn)Python解析器(CPython)時(shí)所引入的一個(gè)概念。就好比C++是一套語(yǔ)言(語(yǔ)法)標(biāo)準(zhǔn),但是可以用不同的編譯器來(lái)編譯成可執(zhí)行代碼。


  有名的編譯器例如GCC,INTEL C++,Visual C++等。Python也一樣,同樣一段代碼可以通過(guò)CPython,PyPy,Psyco等不同的Python執(zhí)行環(huán)境來(lái)執(zhí)行。


  像其中的JPython就沒(méi)有GIL。然而因?yàn)镃Python是大部分環(huán)境下默認(rèn)的Python執(zhí)行環(huán)境。


  所以在很多人的概念里CPython就是Python,也就想當(dāng)然的把GIL歸結(jié)為Python語(yǔ)言的缺陷。


  所以這里要先明確一點(diǎn):GIL并不是Python的特性,Python完全可以不依賴(lài)于GIL


  二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)程


  驗(yàn)證python test.py只會(huì)產(chǎn)生一個(gè)進(jìn)程


  #test.py內(nèi)容
  import os,time
  print(os.getpid())
  time.sleep(1000)
  #打開(kāi)終端執(zhí)行
  python3 test.py
  #在windows下查看
  tasklist|findstr python


  #在linux下下查看


  ps aux|grep python


  在一個(gè)python的進(jìn)程內(nèi),不僅有test.py的主線(xiàn)程或者由該主線(xiàn)程開(kāi)啟的其他線(xiàn)程,還有解釋器開(kāi)啟的垃圾回收等解釋器級(jí)別的線(xiàn)程,總之,所有線(xiàn)程都運(yùn)行在這一個(gè)進(jìn)程內(nèi),毫無(wú)疑問(wèn)


  1、所有數(shù)據(jù)都是共享的,這其中,代碼作為一種數(shù)據(jù)也是被所有線(xiàn)程共享的(test.py的所有代碼以及Cpython解釋器的所有代碼)


  例如:test.py定義一個(gè)函數(shù)work(代碼內(nèi)容如下圖),在進(jìn)程內(nèi)所有線(xiàn)程都能訪(fǎng)問(wèn)到work的代碼,于是我們可以開(kāi)啟三個(gè)線(xiàn)程然后target都指向該代碼,能訪(fǎng)問(wèn)到意味著就是可以執(zhí)行。


  2、所有線(xiàn)程的任務(wù),都需要將任務(wù)的代碼當(dāng)做參數(shù)傳給解釋器的代碼去執(zhí)行,即所有的線(xiàn)程要想運(yùn)行自己的任務(wù),首先需要解決的是能夠訪(fǎng)問(wèn)到解釋器的代碼。


  綜上:


  如果多個(gè)線(xiàn)程的target=work,那么執(zhí)行流程是


  多個(gè)線(xiàn)程先訪(fǎng)問(wèn)到解釋器的代碼,即拿到執(zhí)行權(quán)限,然后將target的代碼交給解釋器的代碼去執(zhí)行


  解釋器的代碼是所有線(xiàn)程共享的,所以垃圾回收線(xiàn)程也可能訪(fǎng)問(wèn)到解釋器的代碼而去執(zhí)行,這就導(dǎo)致了一個(gè)問(wèn)題:對(duì)于同一個(gè)數(shù)據(jù)100,可能線(xiàn)程1執(zhí)行x=100的同時(shí),而垃圾回收?qǐng)?zhí)行的是回收100的操作,解決這種問(wèn)題沒(méi)有什么高明的方法,就是加鎖處理,如下圖的GIL,保證python解釋器同一時(shí)間只能執(zhí)行一個(gè)任務(wù)的代碼

01.png

  三GIL與Lock


  機(jī)智的同學(xué)可能會(huì)問(wèn)到這個(gè)問(wèn)題:Python已經(jīng)有一個(gè)GIL來(lái)保證同一時(shí)間只能有一個(gè)線(xiàn)程來(lái)執(zhí)行了,為什么這里還需要lock?


  首先,我們需要達(dá)成共識(shí):鎖的目的是為了保護(hù)共享的數(shù)據(jù),同一時(shí)間只能有一個(gè)線(xiàn)程來(lái)修改共享的數(shù)據(jù)


  然后,我們可以得出結(jié)論:保護(hù)不同的數(shù)據(jù)就應(yīng)該加不同的鎖。


  最后,問(wèn)題就很明朗了,GIL與Lock是兩把鎖,保護(hù)的數(shù)據(jù)不一樣,前者是解釋器級(jí)別的(當(dāng)然保護(hù)的就是解釋器級(jí)別的數(shù)據(jù),比如垃圾回收的數(shù)據(jù)),后者是保護(hù)用戶(hù)自己開(kāi)發(fā)的應(yīng)用程序的數(shù)據(jù),很明顯GIL不負(fù)責(zé)這件事,只能用戶(hù)自定義加鎖處理,即Lock,如下圖

02.png

  分析:


  1、100個(gè)線(xiàn)程去搶GIL鎖,即搶執(zhí)行權(quán)限


  2、肯定有一個(gè)線(xiàn)程先搶到GIL(暫且稱(chēng)為線(xiàn)程1),然后開(kāi)始執(zhí)行,一旦執(zhí)行就會(huì)拿到lock.acquire()


  3、極有可能線(xiàn)程1還未運(yùn)行完畢,就有另外一個(gè)線(xiàn)程2搶到GIL,然后開(kāi)始運(yùn)行,但線(xiàn)程2發(fā)現(xiàn)互斥鎖lock還未被線(xiàn)程1釋放,于是阻塞,被迫交出執(zhí)行權(quán)限,即釋放GIL


  4、直到線(xiàn)程1重新?lián)尩紾IL,開(kāi)始從上次暫停的位置繼續(xù)執(zhí)行,直到正常釋放互斥鎖lock,然后其他的線(xiàn)程再重復(fù)2 3 4的過(guò)程


  代碼示范


  from threading import Thread,Lock
  import os,time
  def work():
  global n
  lock.acquire()
  temp=n
  time.sleep(0.1)
  n=temp-1
  lock.release()
  if __name__=='__main__':
  lock=Lock()
  n=100
  l=[]
  for i in range(100):
  p=Thread(target=work)
  l.append(p)
  p.start()
  for p in l:
  p.join()
  print(n)#結(jié)果肯定為0,由原來(lái)的并發(fā)執(zhí)行變成串行,犧牲了執(zhí)行效率保證了數(shù)據(jù)安全,不加鎖則結(jié)果可能為99


  四GIL與多線(xiàn)程


  有了GIL的存在,同一時(shí)刻同一進(jìn)程中只有一個(gè)線(xiàn)程被執(zhí)行


  聽(tīng)到這里,有的同學(xué)立馬質(zhì)問(wèn):進(jìn)程可以利用多核,但是開(kāi)銷(xiāo)大,而python的多線(xiàn)程開(kāi)銷(xiāo)小,但卻無(wú)法利用多核優(yōu)勢(shì),也就是說(shuō)python沒(méi)用了,php才是最牛逼的語(yǔ)言?


  別著急啊,老娘還沒(méi)講完呢。


  要解決這個(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)。


  如果你的工廠(chǎng)干的大多數(shù)任務(wù)都要有準(zhǔn)備原材料的過(guò)程(I/O密集型),那么你有再多的工人,意義也不大,還不如一個(gè)人,在等材料的過(guò)程中讓工人去干別的活,


  反過(guò)來(lái)講,如果你的工廠(chǎng)原材料都齊全,那當(dāng)然是工人越多,效率越高


  結(jié)論:


  1、對(duì)計(jì)算來(lái)說(shuō),cpu越多越好,但是對(duì)于I/O來(lái)說(shuō),再多的cpu也沒(méi)用


  2、當(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的多線(xiàn)程到底有無(wú)用武之地


  假設(shè)我們有四個(gè)任務(wù)需要處理,處理方式肯定是要玩出并發(fā)的效果,解決方案可以是:


  方案一:開(kāi)啟四個(gè)進(jìn)程


  方案二:一個(gè)進(jìn)程下,開(kāi)啟四個(gè)線(xiàn)程


  單核情況下,分析結(jié)果:


  如果四個(gè)任務(wù)是計(jì)算密集型,沒(méi)有多核來(lái)并行計(jì)算,方案一徒增了創(chuàng)建進(jìn)程的開(kāi)銷(xiāo),方案二勝


  如果四個(gè)任務(wù)是I/O密集型,方案一創(chuàng)建進(jìn)程的開(kāi)銷(xiāo)大,且進(jìn)程的切換速度遠(yuǎn)不如線(xiàn)程,方案二勝


  多核情況下,分析結(jié)果:


  如果四個(gè)任務(wù)是計(jì)算密集型,多核意味著并行計(jì)算,在python中一個(gè)進(jìn)程中同一時(shí)刻只有一個(gè)線(xiàn)程執(zhí)行用不上多核,方案一勝


  如果四個(gè)任務(wù)是I/O密集型,再多的核也解決不了I/O問(wèn)題,方案二勝


  結(jié)論:


  現(xiàn)在的計(jì)算機(jī)基本上都是多核,python對(duì)于計(jì)算密集型的任務(wù)開(kāi)多線(xiàn)程的效率并不能帶來(lái)多大性能上的提升,甚至不如串行(沒(méi)有大量切換),但是,對(duì)于IO密集型的任務(wù)效率還是有顯著提升的。


  五多線(xiàn)程性能測(cè)試


  如果并發(fā)的多個(gè)任務(wù)是計(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
  if __name__=='__main__':
  l=[]
  print(os.cpu_count())#本機(jī)為4核
  start=time.time()
  for i in range(4):
  p=Process(target=work)#耗時(shí)5s多
  p=Thread(target=work)#耗時(shí)18s多
  l.append(p)
  p.start()
  for p in l:
  p.join()
  stop=time.time()
  print('run time is%s'%(stop-start))
  如果并發(fā)的多個(gè)任務(wù)是I/O密集型:多線(xiàn)程效率高
  from multiprocessing import Process
  from threading import Thread
  import threading
  import os,time
  def work():
  time.sleep(2)
  print('===>')
  if __name__=='__main__':
  l=[]
  print(os.cpu_count())#本機(jī)為4核
  start=time.time()
  for i in range(400):
  #p=Process(target=work)#耗時(shí)12s多,大部分時(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)用:


  多線(xiàn)程用于IO密集型,如socket,爬蟲(chóng),web


  多進(jìn)程用于計(jì)算密集型,如金融分析


  綜上所述,上述內(nèi)容就給大家介紹完畢,希望可以給大家?guī)?lái)一定的幫助。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/128440.html

相關(guān)文章

  • 淺談Python多線(xiàn)程

    摘要:進(jìn)程可創(chuàng)建多個(gè)線(xiàn)程來(lái)執(zhí)行同一程序的不同部分。就緒等待線(xiàn)程調(diào)度。運(yùn)行線(xiàn)程正常運(yùn)行阻塞暫停運(yùn)行,解除阻塞后進(jìn)入狀態(tài)重新等待調(diào)度。消亡線(xiàn)程方法執(zhí)行完畢返回或者異常終止。多線(xiàn)程多的情況下,依次執(zhí)行各線(xiàn)程的方法,前頭一個(gè)結(jié)束了才能執(zhí)行后面一個(gè)。 淺談Python多線(xiàn)程 作者簡(jiǎn)介: 姓名:黃志成(小黃)博客: 博客 線(xiàn)程 一.什么是線(xiàn)程? 操作系統(tǒng)原理相關(guān)的書(shū),基本都會(huì)提到一句很經(jīng)典的話(huà): 進(jìn)程...

    zsirfs 評(píng)論0 收藏0
  • python多線(xiàn)程、、event事件機(jī)制簡(jiǎn)單使用

    摘要:從調(diào)用方法啟動(dòng)線(xiàn)程,到方法執(zhí)行完畢或遇到未處理異常而中斷這段時(shí)間內(nèi),線(xiàn)程是激活的調(diào)用將會(huì)使主調(diào)線(xiàn)程堵塞,直到被調(diào)用線(xiàn)程運(yùn)行結(jié)束或超時(shí)。對(duì)象實(shí)現(xiàn)了簡(jiǎn)單的線(xiàn)程通信機(jī)制,它提供了設(shè)置信號(hào),清除信號(hào),等待等用于實(shí)現(xiàn)線(xiàn)程間的通信。 線(xiàn)程和進(jìn)程 1、線(xiàn)程共享創(chuàng)建它的進(jìn)程的地址空間,進(jìn)程有自己的地址空間2、線(xiàn)程可以訪(fǎng)問(wèn)進(jìn)程所有的數(shù)據(jù),線(xiàn)程可以相互訪(fǎng)問(wèn) 3、線(xiàn)程之間的數(shù)據(jù)是獨(dú)立的 4、子進(jìn)程復(fù)制線(xiàn)程的...

    lowett 評(píng)論0 收藏0
  • 從偽并行 Python 多線(xiàn)程說(shuō)起

    摘要:多個(gè)線(xiàn)程可以同時(shí)執(zhí)行?,F(xiàn)在我們執(zhí)行,嘗試在不同數(shù)量的線(xiàn)程中執(zhí)行這個(gè)函數(shù)。如果線(xiàn)程是真并行,時(shí)間開(kāi)銷(xiāo)應(yīng)該不會(huì)隨線(xiàn)程數(shù)大幅上漲。由此可見(jiàn),確實(shí)是造成偽并行現(xiàn)象的主要因素。小結(jié)由于的存在,大多數(shù)情況下多線(xiàn)程無(wú)法利用多核優(yōu)勢(shì)。 本文首發(fā)于本人博客,轉(zhuǎn)載請(qǐng)注明出處 寫(xiě)在前面 作者電腦有 4 個(gè) CPU,因此使用 4 個(gè)線(xiàn)程測(cè)試是合理的 本文使用的 cpython 版本為 3.6.4 本文使...

    SegmentFault 評(píng)論0 收藏0
  • GIL 已經(jīng)被殺死了么?

    摘要:酷睿代在年取代了奔騰,主頻遠(yuǎn)低于此。該詞被敏捷開(kāi)發(fā)團(tuán)隊(duì)使用較多,含義與形式會(huì)略有不同,更改已經(jīng)開(kāi)始將垃圾收集器的狀態(tài)轉(zhuǎn)到解釋器,因此每個(gè)子解釋器將擁有它自己的本該如此。結(jié)論死亡了嗎對(duì)于單線(xiàn)程的應(yīng)用程序,仍然存活。showImg(https://user-gold-cdn.xitu.io/2019/5/19/16ad09f554fdf443); 本文原創(chuàng)并首發(fā)于公眾號(hào)【Python貓】,未經(jīng)授...

    番茄西紅柿 評(píng)論0 收藏0
  • GIL 已經(jīng)被殺死了么?

    摘要:酷睿代在年取代了奔騰,主頻遠(yuǎn)低于此。該詞被敏捷開(kāi)發(fā)團(tuán)隊(duì)使用較多,含義與形式會(huì)略有不同,更改已經(jīng)開(kāi)始將垃圾收集器的狀態(tài)轉(zhuǎn)到解釋器,因此每個(gè)子解釋器將擁有它自己的本該如此。結(jié)論死亡了嗎對(duì)于單線(xiàn)程的應(yīng)用程序,仍然存活。showImg(https://user-gold-cdn.xitu.io/2019/5/19/16ad09f554fdf443); 本文原創(chuàng)并首發(fā)于公眾號(hào)【Python貓】,未經(jīng)授...

    pkwenda 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<