Python全局鎖中如何合理運(yùn)用多線程(多進(jìn)程)
Python全局鎖
(1)全局鎖導(dǎo)致的問題
全局鎖的英文簡(jiǎn)稱是GIL,全稱是Global Interpreter Lock(全局解釋器鎖),來源是python設(shè)計(jì)之初的考慮,為了數(shù)據(jù)安全所做的決定,每個(gè)線程在執(zhí)行時(shí)候都需要先獲取GIL,保證同一時(shí)刻只有一個(gè)線程可以執(zhí)行代碼,即同一時(shí)刻只有一個(gè)線程使用CPU,也就是說多線程并不是真正意義上的同時(shí)執(zhí)行。
每個(gè)CPU在同一時(shí)間只能執(zhí)行一個(gè)線程(在單核CPU下的多線程其實(shí)都只是并發(fā),不是并行,并發(fā)和并行從宏觀上來講都是同時(shí)處理多路請(qǐng)求的概念。但并發(fā)和并行又有區(qū)別,并行是指兩個(gè)或者多個(gè)事件在同一時(shí)刻發(fā)生(多個(gè)CPU同時(shí)執(zhí)行某個(gè)任務(wù));而并發(fā)是指兩個(gè)或多個(gè)事件在同一時(shí)間間隔內(nèi)發(fā)生。)
在Python多線程下,每個(gè)線程的執(zhí)行方式:
1、獲取GIL
2、執(zhí)行代碼直到sleep或者是python虛擬機(jī)將其掛起。
3、釋放GIL
可見,某個(gè)線程想要執(zhí)行,必須先拿到GIL,我們可以把GIL看作是“通行證”,并且在一個(gè)python進(jìn)程中,GIL只有一個(gè)。拿不到通行證的線程,就不允許進(jìn)入CPU執(zhí)行。
在Python2.x里,GIL的釋放邏輯是當(dāng)前線程遇見IO操作或者ticks計(jì)數(shù)達(dá)到100(ticks可以看作是Python自身的一個(gè)計(jì)數(shù)器,專門做用于GIL,每次釋放后歸零,這個(gè)計(jì)數(shù)可以通過 sys.setcheckinterval 來調(diào)整),進(jìn)行釋放。
而每次釋放GIL鎖,線程進(jìn)行鎖競(jìng)爭(zhēng)、切換線程,會(huì)消耗資源。并且由于GIL鎖存在,python里一個(gè)進(jìn)程永遠(yuǎn)只能同時(shí)執(zhí)行一個(gè)線程(拿到GIL的線程才能執(zhí)行),這就是為什么在多核CPU上,python的多線程效率并不高。
(2)在有全局鎖的情況下如何運(yùn)行多線程、多進(jìn)程
在這里我們進(jìn)行分類討論:
1、CPU密集型代碼(各種循環(huán)處理、計(jì)數(shù)等等),在這種情況下,由于計(jì)算工作多,ticks計(jì)數(shù)很快就會(huì)達(dá)到閾值,然后觸發(fā)GIL的釋放與再競(jìng)爭(zhēng)(多個(gè)線程來回切換當(dāng)然是需要消耗資源的),所以python下的多線程對(duì)CPU密集型代碼并不友好,此時(shí)可以采用多進(jìn)程形式實(shí)現(xiàn)多任務(wù)。
2、IO密集型代碼(文件處理、網(wǎng)絡(luò)爬蟲等),多線程能夠有效提升效率(單線程下有IO操作會(huì)進(jìn)行IO等待,造成不必要的時(shí)間浪費(fèi),而開啟多線程能在線程A等待時(shí),自動(dòng)切換到線程B,可以不浪費(fèi)CPU的資源,從而能提升程序執(zhí)行效率)。所以python的多線程對(duì)IO密集型代碼比較友好。
而在python3.x中,GIL不使用ticks計(jì)數(shù),改為使用計(jì)時(shí)器(執(zhí)行時(shí)間達(dá)到閾值后,當(dāng)前線程釋放GIL),這樣對(duì)CPU密集型程序更加友好,但依然沒有解決GIL導(dǎo)致的同一時(shí)間只能執(zhí)行一個(gè)線程的問題,所以效率依然不盡如人意。
請(qǐng)注意:多核多線程比單核多線程更差,原因是單核下多線程,每次釋放GIL,喚醒的那個(gè)線程都能獲取到GIL鎖,所以能夠無縫執(zhí)行,但多核下,CPU0釋放GIL后,其他CPU上的線程都會(huì)進(jìn)行競(jìng)爭(zhēng),但GIL可能會(huì)馬上又被CPU0拿到,導(dǎo)致其他幾個(gè)CPU上被喚醒后的線程會(huì)醒著等待到切換時(shí)間后又進(jìn)入待調(diào)度狀態(tài),這樣會(huì)造成線程顛簸(thrashing),導(dǎo)致效率更低
回到最開始的問題:經(jīng)常我們會(huì)聽到老手說:“python下想要充分利用多核CPU,就用多進(jìn)程”,原因是什么呢?
原因是:每個(gè)進(jìn)程有各自獨(dú)立的GIL,互不干擾,這樣就可以真正意義上的并行執(zhí)行,所以在python中,多進(jìn)程的執(zhí)行效率優(yōu)于多線程(僅僅針對(duì)多核CPU而言)。
(3)代碼實(shí)例
使用一個(gè)線程去計(jì)數(shù)
#encoing:utf-8 import threading import time def test_counter(): i = 0 for _ in range(100000000): i += 1 return True def main(): start_time = time.time() for tid in range(2): t1 = threading.Thread(target=test_counter) t1.start() t1.join() end_time = time.time() print("Total time:{}".format(end_time-start_time)) if __name__ == "__main__": main()
結(jié)果:
使用2個(gè)線程,去執(zhí)行非IO操作
#encoding:utf-8 import threading import time def test_counter(): i = 0 for _ in range(100000000): i += 1 return True def main(): thread_array = {} start_time = time.time() for tid in range(2): t = threading.Thread(target=test_counter) t.start() thread_array[tid] = t for i in range(2): thread_array[i].join() end_time = time.time() print("Total time:{}".format(end_time-start_time)) if __name__ == "__main__": main()
結(jié)果:
通過上面的代碼可以得出,對(duì)于非IO類型操作,多線程為了獲得GIL去相互競(jìng)爭(zhēng),導(dǎo)致程序執(zhí)行效率更低,所以我們要根據(jù)實(shí)際的業(yè)務(wù)功能情況,來確定使用多線程、多進(jìn)程!
總結(jié)
以上所述是小編給大家介紹的在Python全局鎖中如何合理運(yùn)用多線程(多進(jìn)程),希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
python實(shí)現(xiàn)屏保程序(適用于背單詞)
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)屏保程序,適用于背單詞,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07淺談PyTorch的數(shù)據(jù)讀取機(jī)制Dataloader與Dataset
這篇文章主要介紹了淺談PyTorch的數(shù)據(jù)讀取機(jī)制Dataloader與Dataset,DataLoader的作用是構(gòu)建一個(gè)可迭代的數(shù)據(jù)裝載器,每次執(zhí)行循環(huán)的時(shí)候,就從中讀取一批Batchsize大小的樣本進(jìn)行訓(xùn)練,需要的朋友可以參考下2023-07-07如何將numpy二維數(shù)組中的np.nan值替換為指定的值
這篇文章主要介紹了將numpy二維數(shù)組中的np.nan值替換為指定的值操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-05-05wxPython電子表格功能wx.grid實(shí)例教程
這篇文章主要介紹了wxPython電子表格功能wx.grid實(shí)例教程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11巧妙使用Python裝飾器處理if...elif...else
大家好,今天在 Github 閱讀 EdgeDB[1] 的代碼,發(fā)現(xiàn)它在處理大量if…elif…else的時(shí)候,巧妙地使用了裝飾器,方法設(shè)計(jì)精巧,分享給大家一下,歡迎收藏學(xué)習(xí),喜歡點(diǎn)贊支持2021-11-11Python3使用Selenium獲取session和token方法詳解
這篇文章主要介紹了Python3使用Selenium獲取session和token方法詳解,需要的朋友可以參考下2021-02-02Python保存數(shù)據(jù)到文件的實(shí)現(xiàn)方式
這篇文章主要介紹了Python保存數(shù)據(jù)到文件的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11