1. 程式人生 > >python學習筆記- day10-【問題: python為什麽python的多線程不能利用多核CPU?】

python學習筆記- day10-【問題: python為什麽python的多線程不能利用多核CPU?】

例如 currency 視頻 stat 解碼 核心數 __name__ args 制作

為什麽python的多線程不能利用多核CPU,但是咱們在寫代碼的時候,多線程的確是在並發,而且還比單線程快。

一、python的多線程不能利用多核CPU?

原因:

因為GIL,python只有一個GIL,運行python時,就要拿到這個鎖才能執行,在遇到I/O 操作時會釋放這把鎖。
如果是純計算的程序,沒有 I/O 操作,解釋器會每隔100次操作就釋放這把鎖,讓別的線程有機會 執行(這個次數可以通sys.setcheckinterval
來調整)同一時間只會有一個獲得GIL線程在跑,其他線程都處於等待狀態
1、如果是CPU密集型代碼(循環、計算等),由於計算工作量多和大,計算很快就會達到100,然後觸發GIL的釋放與在競爭,多個線程來回切換損耗資源,
所以在多線程遇到CPU密集型代碼時,單線程會比較快
2、如果是I\O密集型代碼(文件處理、網絡爬蟲),開啟多線程實際上是並發(不是並行),IO操作會進行IO等待,線程A等待時,自動切換到線程B,
這樣就提升了效率

二、其他原理解釋

轉:鏈接:https://www.zhihu.com/question/23474039/answer/24695447

地說就是作為可能是僅有的支持多線程的解釋型語言(perl的多線程是殘疾,PHP沒有多線程),Python的多線程是有compromise的,在任意時間只有一個Python解釋器在解釋Python bytecode。

如評論指出,Ruby也是有thread支持的,而且至少Ruby MRI是有GIL的。如果你的代碼是CPU密集型,多個線程的代碼很有可能是線性執行的。所以這種情況下多線程是雞肋,效率可能還不如單線程因為有context switch但是:
如果你的代碼是IO密集型,多線程可以明顯提高效率。例如制作爬蟲,絕大多數時間爬蟲是在等待socket返回數據。這個時候C代碼裏是有release GIL的,最終結果是某個線程等待IO的時候其他線程可以繼續執行。
反過來講:你就不應該用Python寫CPU密集型的代碼…效率擺在那裏…如果確實需要在CPU密集型的代碼裏用concurrent,就去用multiprocessing庫。這個庫是基於multi process實現了類multi thread的API接口,
並且用pickle部分地實現了變量共享。再加一條,如果你不知道你的代碼到底算CPU密集型還是IO密集型,

教你個方法:multiprocessing這個module有一個dummy的sub module,它是基於multithread實現了multiprocessing的API。

假設你使用的是multiprocessing的Pool,是使用多進程實現了concurrency
from multiprocessing
import Pool 如果把這個代碼改成下面這樣,就變成多線程實現concurrency

from multiprocessing.dummy
import Pool 兩種方式都跑一下,哪個速度快用哪個就行了。

轉: 鏈接:https://www.zhihu.com/question/23474039/answer/269526476


在介紹Python中的線程之前,先明確一個問題,Python中的多線程是假的多線程! 為什麽這麽說,我們先明確一個概念,全局解釋器鎖(GIL)。

Python代碼的執行由Python虛擬機(解釋器)來控制。Python在設計之初就考慮要在主循環中,同時只有一個線程在執行,就像單CPU的系統中運行多個進程那樣,內存中可以存放多個程序,但任意時刻,只有一個程序在CPU中運行。
同樣地,雖然Python解釋器可以運行多個線程,只有一個線程在解釋器中運行。對Python虛擬機的訪問由全局解釋器鎖(GIL)來控制,正是這個鎖能保證同時只有一個線程在運行。

在多線程環境中,Python虛擬機按照以下方式執行。

1.設置GIL。
2.切換到一個線程去執行。
3.運行。
4.把線程設置為睡眠狀態。
5.解鎖GIL。
6.再次重復以上步驟。

對所有面向I/O的(會調用內建的操作系統C代碼的)程序來說,GIL會在這個I/O調用之前被釋放,以允許其他線程在這個線程等待I/O的時候運行。如果某線程並未使用很多I/O操作,它會在自己的時間片內一直占用處理器和GIL。
也就是說,I/O密集型的Python程序比計算密集型的Python程序更能充分利用多線程的好處。我們都知道,比方我有一個4核的CPU,那麽這樣一來,在單位時間內每個核只能跑一個線程,然後時間片輪轉切換。
但是Python不一樣,它不管你有幾個核,單位時間多個核只能跑一個線程,然後時間片輪轉。看起來很不可思議?但是這就是GIL搞的鬼。任何Python線程執行前,必須先獲得GIL鎖,然後,每執行100條字節碼,解釋器就自動釋放GIL鎖,
讓別的線程有機會執行。這個GIL全局鎖實際上把所有線程的執行代碼都給上了鎖,所以,多線程在Python中只能交替執行,即使100個線程跑在100核CPU上,也只能用到1個核。通常我們用的解釋器是官方實現的CPython,要真正利用多核,除非重寫一個不帶GIL的解釋器。

我們不妨做個試驗:

#coding=utf-8 from multiprocessing import Pool from threading import Thread from multiprocessing import Process def loop():   while True:     pass if __name__ == ‘__main__‘:   for i in range(3):     t = Thread(target=loop)     t.start()   while True:     pass

我們發現CPU利用率並沒有占滿,大致相當於單核水平。

而如果我們變成進程呢?

我們改一下代碼:

#coding=utf-8 from multiprocessing import Pool from threading import Thread from multiprocessing import Process def loop():   while True:     pass if __name__ == ‘__main__‘:   for i in range(3):     t = Process(target=loop)     t.start()   while True:     pass

結果直接飆到了100%,說明進程是可以利用多核的!

為了驗證這是Python中的GIL搞得鬼,我試著用Java寫相同的代碼,開啟線程,我們觀察一下

package com.darrenchan.thread;

public class TestThread {
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    while (true) {

                    }
                }
            }).start();
        }
        while(true){

        }
    }
}

由此可見,Java中的多線程是可以利用多核的,這是真正的多線程!而Python中的多線程只能利用單核,這是假的多線程!

三、解決方法

轉:鏈接:https://www.zhihu.com/question/23474039/answer/269526476 就如此?我們沒有辦法在Python中利用多核?當然可以!剛才的多進程算是一種解決方案,還有一種就是調用C語言的鏈接庫。對所有面向I/O的(會調用內建的操作系統C代碼的)程序來說,GIL會在這個I/O調用之前被釋放,以允許其他線程在這個線程等待I/O的時候運行。我們可以把一些 計算密集型任務用C語言編寫,然後把.so鏈接庫內容加載到Python中,因為執行C代碼,GIL鎖會釋放,這樣一來,就可以做到每個核都跑一個線程的目的!

可能有的小夥伴不太理解什麽是計算密集型任務,什麽是I/O密集型任務?

計算密集型任務的特點是要進行大量的計算,消耗CPU資源,比如計算圓周率、對視頻進行高清解碼等等,全靠CPU的運算能力。這種計算密集型任務雖然也可以用多任務完成,但是任務越多,花在任務切換的時間就越多,CPU執行任務的效率就越低,所以,要最高效地利用CPU,計算密集型任務同時進行的數量應當等於CPU的核心數。

計算密集型任務由於主要消耗CPU資源,因此,代碼運行效率至關重要。Python這樣的腳本語言運行效率很低,完全不適合計算密集型任務。對於計算密集型任務,最好用C語言編寫。

第二種任務的類型是IO密集型,涉及到網絡、磁盤IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低於CPU和內存的速度)。對於IO密集型任務,任務越多,CPU效率越高,但也有一個限度。常見的大部分任務都是IO密集型任務,比如Web應用。

IO密集型任務執行期間,99%的時間都花在IO上,花在CPU上的時間很少,因此,用運行速度極快的C語言替換用Python這樣運行速度極低的腳本語言,完全無法提升運行效率。對於IO密集型任務,最合適的語言就是開發效率最高(代碼量最少)的語言,腳本語言是首選,C語言最差。

綜上,Python多線程相當於單核多線程,多線程有兩個好處:CPU並行,IO並行,單核多線程相當於自斷一臂。所以,在Python中,可以使用多線程,但不要指望能有效利用多核。如果一定要通過多線程利用多核,那只能通過C擴展來實現,不過這樣就失去了Python簡單易用的特點。不過,也不用過於擔心,Python雖然不能利用多線程實現多核任務,但可以通過多進程實現多核任務。多個Python進程有各自獨立的GIL鎖,互不影響。

python學習筆記- day10-【問題: python為什麽python的多線程不能利用多核CPU?】