Python的并發(fā)編程(七)- 如何規(guī)避GIL帶來的限制

我們已經(jīng)聽說過全局解釋器鎖(GIL)哩掺,擔(dān)心會(huì)影響到多線程的性能垄懂。盡管Python完全支持多線程編程虚汛,但是在解釋器的C語言實(shí)現(xiàn)中围来,有一部分并不是線程安全的跺涤,因此不能完全支持并發(fā)執(zhí)行。

事實(shí)上管钳,解釋器被一個(gè)稱為全局解釋器鎖的東西保護(hù)著钦铁,在任意時(shí)刻只允許一個(gè)Python線程投入執(zhí)行。GIL帶來的最明顯的影響就是多線程的python程序無法充分利用多個(gè)CPU核心帶來的優(yōu)勢(shì)(即才漆,一個(gè)采用多線程技術(shù)的計(jì)算密集型應(yīng)用只能在一個(gè)CPU上運(yùn)行)牛曹。

在討論規(guī)避GIL的常用方案之前,需要重點(diǎn)強(qiáng)調(diào)的是醇滥,GIL只會(huì)對(duì)CPU密集型的程序產(chǎn)生影響(主要完成計(jì)算任務(wù)的程序)黎比。如果我們的程序主要是在做I/O操作,比如處理網(wǎng)絡(luò)連接鸳玩,那么選擇多線程技術(shù)常常是一個(gè)明智的選擇阅虫。因?yàn)樗麄兇蟛糠謺r(shí)間都花在等待對(duì)放發(fā)起連接上了。實(shí)際上可以創(chuàng)建數(shù)以千計(jì)的Python線程都沒問題不跟。在現(xiàn)代的計(jì)算機(jī)上運(yùn)行這么多線程是不會(huì)有問題的颓帝。

在理解這部分的內(nèi)容的時(shí)候,你可以將存在全局解釋器鎖的Python解釋器看作一個(gè)加油站窝革。

GIL.png

I/O密集型任務(wù)中购城,你可以理解為有多輛小轎車駛?cè)肓思佑驼具M(jìn)行加油,雖然不知道哪個(gè)先加完虐译,但是會(huì)比只有一個(gè)加油點(diǎn)的加油站順序很快瘪板。如果是計(jì)算密集型的任務(wù)呢,就是當(dāng)這輛車駛?cè)爰佑驼镜臅r(shí)候漆诽,加油站所有的資源都用來服務(wù)這一輛車了侮攀,別的車只能等待锣枝。

當(dāng)然上述比喻只是為了大家方便理解兩種不同類型任務(wù)的區(qū)別。

對(duì)于CPU密集型的程序兰英,我們需要對(duì)問題的本質(zhì)做些研究撇叁,例如,仔細(xì)選擇底層使用的算法箭昵,這可能會(huì)比嘗試將一個(gè)沒有優(yōu)化過的算法用多線程來并行處理所帶來的性能提升要多得多税朴。同樣的,由于Python是解釋型語言家制,往往只需要簡(jiǎn)單的將性能關(guān)鍵的代碼轉(zhuǎn)移到C語言擴(kuò)展的模塊中就可能得到極大的性能提升。類似NumPy這樣的擴(kuò)展模塊對(duì)于加速涉及數(shù)組數(shù)據(jù)的特定計(jì)算也是非常高效的泡一。最后但同樣重要的是颤殴,還可以嘗試使用其他的解釋器實(shí)現(xiàn),比如說使用了JIT編譯優(yōu)化技術(shù)的PYPY鼻忠。

同樣值得指出的是涵但,使用多線程技術(shù)并不是為了獲得性能的提升。一個(gè)CPU密集型的程序可能會(huì)用多線程來管理圖形用戶界面帖蔓、網(wǎng)絡(luò)連接或者其他類型的服務(wù)矮瘟。在這種情況下GIL實(shí)際上會(huì)帶來更多的問題。因?yàn)槿绻巢糠执a持有GIL鎖的時(shí)間過長(zhǎng)塑娇,那就會(huì)導(dǎo)致其他非CPU密集型的線程都阻塞住澈侠。實(shí)際上埋酬,一個(gè)寫的很糟糕的C擴(kuò)展模塊會(huì)讓這個(gè)問題更加嚴(yán)重,盡管代碼中C實(shí)現(xiàn)的部分會(huì)比之前運(yùn)行的快写妥。

說這么多,要規(guī)避GIL的限制主要有兩種常用的策略珍特。

  1. 如果完全使用Python來編程祝峻,可以使用multiprocessin模塊來創(chuàng)建進(jìn)程池,把它當(dāng)作協(xié)處理器來使用扎筒。看看下面的例子:

    # 大量的計(jì)算任務(wù) (CPU bound)
    def some_work(args):
        # ...
        result = args
        return result
    
    # 線程函數(shù)
    def some_thread():
        while True:
            # ...
            args = None
            r = some_work(args)
            # ...
    

    上面是使用線程去處理這個(gè)任務(wù)砸琅,下面我們可以將代碼改為進(jìn)程池的方式:

    pool = None
    
    # 大量的計(jì)算任務(wù) (CPU bound)
    def some_work(args):
        # ...
        result = args
        return result
    
    # 線程函數(shù)
    def some_thread():
        while True:
            # ...
            r = pool.apply(some_work, (args))
            # ...
            
    if __name__ == "__main__":
        import multiprocessing
        pool = multiprocessing.Pool()
    

    使用進(jìn)程池的例子通過一個(gè)巧妙的辦法避開了GIL的限制。每當(dāng)有線程要執(zhí)行CPU密集型任務(wù)的時(shí)候症脂,就把任務(wù)提交到進(jìn)程池中谚赎,然后進(jìn)程池將任務(wù)轉(zhuǎn)交給運(yùn)行在另一個(gè)進(jìn)程中的python解釋器淫僻。當(dāng)線程等待結(jié)果的時(shí)候就會(huì)釋放GIL。此外壶唤,由于計(jì)算是在另一個(gè)單獨(dú)的解釋器中進(jìn)行的,這就不再受到GIL的限制了闸盔,在多核系統(tǒng)上,將會(huì)發(fā)現(xiàn)采用這種技術(shù)能夠輕易利用到所有的CPU核心迎吵。

  2. 第二種方式是把重點(diǎn)放在C語言的擴(kuò)展編程上。主要思想就是將計(jì)算密集的任務(wù)轉(zhuǎn)移到C語言中击费,使其獨(dú)立于Python,在C代碼中釋放GIL蔫巩,這里由于我也不會(huì)C,就不做示例了圆仔。

在我們面對(duì)多線程程序性能問題的時(shí)候,不能去抱怨GIL是所有問題的根源坪郭。但是,這么做只是一種短視和幼稚的行為截粗。舉個(gè)例子,在多線程網(wǎng)絡(luò)程序中出現(xiàn)神秘的“僵死”現(xiàn)象绸罗,這種現(xiàn)象可能是別的原因造成的,和GIL沒有一點(diǎn)關(guān)系珊蟀。所以要先認(rèn)真研究自己的代碼,判斷GIL是否才是問題的關(guān)鍵育灸。CPU密集型的處理才是需要考慮GIL,I/O密集的處理則不必要考慮磅崭。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市柔逼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌愉适,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件维咸,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡癌蓖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門倒槐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事两残。” “怎么了人弓?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)崔赌。 經(jīng)常有香客問我,道長(zhǎng)健芭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任若贮,我火速辦了婚禮,結(jié)果婚禮上谴麦,老公的妹妹穿的比我還像新娘。我一直安慰自己伸头,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布面哼。 她就那樣靜靜地躺著野宜,像睡著了一般精绎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上代乃,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音搁吓,去河邊找鬼。 笑死堕仔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的摩骨。 我是一名探鬼主播通贞,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼恼五,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了灾馒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤轨功,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后古涧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蒿褂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年卒暂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了啄栓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片也祠。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖诈嘿,靈堂內(nèi)的尸體忽然破棺而出削葱,到底是詐尸還是另有隱情淳梦,我是刑警寧澤析砸,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布爆袍,位于F島的核電站,受9級(jí)特大地震影響陨囊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜘醋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望啸罢。 院中可真熱鬧,春花似錦胎食、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谤专,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間置侍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來泰國打工蜡坊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秕衙。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像鹦牛,于是被迫代替她去往敵國和親搞糕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子曼追,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容