首頁 > 軟體

python多執行緒對多核cpu的利用解析

2022-07-01 14:02:02

引言

我們經常聽到"因為GIL的存在,python的多執行緒不能利用多核CPU",現在我們暫且不提GIL,python能不能利用多核cpu,今天我做了一個實驗,程式碼很簡單如下所示

while 1:
    pass

沒有執行這段程式碼前cpu狀態

執行之後的狀態

下面兩張圖是執行之後的狀態,當然這只是兩張比較有代表性的圖,截圖間隔有十幾秒的樣子

根據第一張圖我們發現cpu1、cpu3的負載有明顯增長,我們可以得出python執行緒是可以利用多核cpu的結論,之前一直以為python執行後會繫結cpu其中的一個核心現在看來並不是這個樣子。第二張圖就比較有意思了cpu2滿載了,這又是為什麼呢?

想來想去應該是linux中cpu對程序的親和性導致的,這種親和性是軟性的並不是強制的,這也就解釋了為什麼第一張圖中是多cpu在負載。

ok為了更直觀的看出python執行緒能夠利用多核cpu,我們改下程式碼,換一種方式再來看下

import os
while 1:
    print os.getpid() # 輸出程序號

執行程式碼結果

一目瞭然,執行緒的確在不同的核心上切換。

現在我們回過頭看下那句經典的話"因為GIL的存在,python的多執行緒不能利用多核CPU",這句話很容易讓人理解成GIL會讓python在一個核心上執行,有了今天的例子我們再來重新理解這句話,GIL的存在讓python在同一時刻只能有一個執行緒在執行,這毋庸置疑,但是它並沒有給執行緒鎖死或者說指定只能在某個cpu上執行,另外我需要說明一點的是GIL是與程序對應的,每個程序都有一個GIL。

python執行緒的執行流程理解

執行緒 ——>搶GIL——>CPU

這種執行流程導致了CPU密集型的多執行緒程式雖然能夠利用多核cpu時跟單核cpu是差不多的,並且由於多個執行緒搶GIL這個環節導致執行效率<=單執行緒。

看到這可能會讓人產生一種錯覺,有了GIL後python是執行緒安全的,好像根本不需要執行緒鎖,而實際情況是執行緒拿到CPU資源後並不是一直執行的,python直譯器在執行了該執行緒100條位元組碼(注意是位元組碼不是程式碼)時會釋放掉該執行緒的GIL,如果這時候沒有加鎖那麼其他執行緒就可能修改該執行緒用到的資源;

遇到IO也會釋放GIL

另外一個問題是遇到IO也會釋放GIL,下面是這兩種情況的例子

import threading
a = []
def m1():
    for _ in range(100000):
        a.append(1)
def m2():
    for _ in range(100000):
        a.append(2)
def check():
    """
    檢查a是否有序
    """
    for i in range(len(a)):
        if i != 0:
            if a[i] &lt; a[i-1]:
                print a[i-1], a[i]
                return False
    return True
t1 = threading.Thread(target=m1)
t2 = threading.Thread(target=m2)
t1.start()
t2.start()
t1.join()
t2.join()
print check()

預期1111...22222...,截圖顯示跟預期的不同

import threading
text1 = '1' * 10000
text2 = '2' * 10000
def write(text):
    with open('test.txt', 'a') as f:
        f.write(text)
def m1():
    write(text1)
def m2():
    write(text2)
t1 = threading.Thread(target=m1)
t2 = threading.Thread(target=m2)
t1.start()
t2.start()
t1.join()
t2.join()

test.txt截圖

最後結論是,因為GIL的存在,python的多執行緒雖然可以利用多核CPU,但並不能讓多個核同時工作。

以上就是python多執行緒對多核cpu的利用解析的詳細內容,更多關於python多執行緒利用多核cpu的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com