第37條 線程執行阻塞式I/O,但不能用來做平行計算

標準的Python實現叫做CPythonCPython分兩步用來運行Python程序:

  • 1.首先,將文本形式的源代碼解析並編譯成字節碼;
  • 2.然後,用一種基於棧的解釋器運行這份字節碼。

執行Python程序時,字節碼解釋器必須保持協調一直的狀態。Python採用全局解釋器鎖(GIL)機制來實現這種協調性。

GIL實際上是一把互斥鎖,用來防止CPython受到搶佔式多線程切換操作的干擾。但是,GIL有一個顯著的缺點在於,C++Java等語言的多線程程序可以充分利用CPU的多核,而Python編寫的多線程程序,受到GIL保護,同一時刻,只有一條線程可以執行

案例1:利用Python進行因數分解算法。

from time import  time

def factorize(number):
    for i in range(1, number+1):
        if number % i == 0:
            yield i
            
if __name__ == '__main__':
    numbers = [2149000, 3902300, 8390202, 10008375]
    start = time()
    for number in numbers:
        list(factorize(number))
    end = time()
    print("Spend time %.3f seconds" % (end-start))

輸出結果:

Spend time 2.022 seconds

改成使用Python的多線程程序:

from threading import Thread
from time import time

def factorize(number):
    for i in range(1, number+1):
        if number % i == 0:
            yield i
            
class FactorizeThread(Thread): ##繼承Thread
    def __init__(self,number):
        super().__init__()
        self.number = number
    def run(self):
        self.factors = list(factorize(self.number))
        
if __name__ == '__main__':
    numbers = [2149000, 3902300, 8390202, 10008375]
    start = time()
    threads = []
    for number in numbers:
        thread = FactorizeThread(number)
        thread.start()
        threads.append(thread)
        
    for thread in threads:
        thread.join()
        
    end = time()
    print('Spend time %.3f seconds' %(end-start))

輸出結果:

Spend time 2.570 seconds

可以看出,多線程程序執行所花費的時間竟然更長。這樣的結果表明,標準的CPython解釋器的多線程程序受到GIL的影響。

儘管Python的多線程很雞肋,但是仍然可以應用在阻塞式的IO操作中,例如讀寫文件,網絡通訊等操作。同時,Python在多個線程中執行系統調用時,其可以實現平行地執行。Python線程在執行系統調用的時候會釋放GIL鎖,並且一直等到執行完畢纔會重新獲取它,所以GIL是不會影響系統調用的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章