隨著 CPU 效能提升趨緩,多核心處理成為提升系統效能的關鍵。Python 提供執行緒與行程兩種機制實作多工,本文著重於多執行緒的應用,探討如何利用 threading 模組在多核心 CPU 環境下提升程式效能。由於 Python 主要實作 CPython 的特性,多執行緒的運用會受到全域性直譯器鎖(GIL)的影響,因此需注意執行緒同步與資源競爭等議題。本文將以實際程式碼示範如何啟動執行緒、設定守護執行緒以及進行多執行緒計算,並輔以圖表說明執行緒運作流程,幫助讀者理解 Python 多執行緒的實務應用。

技術實作

技術實作是服務邊界設定的另一個重要方面。目前,Web 服務根據 HTTP 協定是最常見的服務型別。第 9 章將詳細討論如何構建 REST API。

CPU 縮放

隨著 CPU 不再無限加速,使用多個 CPU 成為提高可擴充套件性的最佳途徑。這意味著需要在程式中引入並發和平行性,這是一項具有挑戰性的任務。然而,一旦正確實作,它確實可以增加總吞吐量。

Python 提供兩種選擇來跨多個本地 CPU 分佈工作負載:執行緒或程式。它們都伴有挑戰,其中一些與 Python 無關,而一些只與其主要實作(即 CPython)相關。

使用執行緒

Python 中的執行緒是一種良好的方法,可以讓函式與其他函式並發執行。如果系統不支援多個處理器,執行緒將按照計劃順序執行。然而,如果有多個 CPU 可用,執行緒可以在多個處理單元上執行,仍然由計劃決定。 Python 中只有一個執行緒,即主執行緒,它執行 Python 應用程式。要啟動另一個執行緒,Python 提供了 threading 模組。

啟動新執行緒

import threading

def print_something(something):
    print(something)

t = threading.Thread(target=print_something, args=("hello",))
t.start()
print("thread started")
t.join()

如果您多次執行示例 2.1 中的程式,您會注意到輸出可能每次都不同。在我的筆記型電腦上,執行此操作會產生以下輸出:

$ python examples/chapter2-cpu-scaling/threading-start.py
hello
thread started
$ python examples/chapter2-cpu-scaling/threading-start.py

內容解密:

上述程式碼示範瞭如何使用 threading 模組啟動新執行緒。threading.Thread 類別用於建立新執行緒,target 引數指定要執行的函式,args 引數指定傳遞給該函式的引數。start() 方法啟動新執行緒,join() 方法等待執行緒完成。

圖表翻譯:

  flowchart TD
    A[主執行緒] --> B[啟動新執行緒]
    B --> C[執行新執行緒]
    C --> D[完成新執行緒]
    D --> E[主執行緒繼續]

此圖表示了主執行緒啟動新執行緒、執行新執行緒、完成新執行緒以及主執行緒繼續執行的過程。

多執行緒基礎

多執行緒是指在一個程式中同時執行多個執行緒,以提高程式的效率和回應速度。Python 的 threading 模組提供了建立和管理執行緒的功能。

建立執行緒

要建立一個執行緒,可以使用 threading.Thread 類別,並傳入一個可呼叫物件(如函式)作為 target 引數。例如:

import threading

def print_something(something):
    print(something)

t = threading.Thread(target=print_something, args=("hello",))
t.start()
print("thread started")

這個例子建立了一個執行緒,執行 print_something 函式,並傳入 "hello" 作為引數。

守護執行緒

如果你不想等待執行緒完成,可以將其設為守護執行緒(daemon)。守護執行緒在主執行緒離開時會自動終止。

t = threading.Thread(target=print_something, args=("hello",))
t.daemon = True
t.start()
print("thread started")

這個例子設定了守護執行緒,因此主執行緒不需要等待其完成。

多執行緒計算

以下是使用多執行緒計算的例子:

import random
import threading

results = []

def compute():
    results.append(sum([random.randint(1, 100) for i in range(1000000)]))

workers = [threading.Thread(target=compute) for x in range(8)]
for worker in workers:
    worker.start()
for worker in workers:
    worker.join()

這個例子建立了 8 個執行緒,每個執行緒計算 1 百萬個隨機整數的總和。結果儲存在 results 列表中。

Mermaid 圖表

  flowchart TD
    A[主執行緒] --> B[建立執行緒]
    B --> C[執行執行緒]
    C --> D[等待執行緒完成]
    D --> E[主執行緒離開]
    E --> F[守護執行緒終止]

圖表翻譯:

這個圖表展示了主執行緒建立和管理執行緒的過程。主執行緒建立一個新的執行緒,然後執行它。主執行緒可以等待執行緒完成,也可以設定守護執行緒讓其在主執行緒離開時自動終止。

內容解密:

多執行緒可以提高程式的效率和回應速度,但也需要注意執行緒之間的同步和溝通問題。在 Python 中,可以使用 threading 模組來建立和管理執行緒。透過設定守護執行緒,可以讓主執行緒離開時自動終止子執行緒。

從系統資源利用的角度來看,有效運用多核心 CPU 提升應用程式效能變得至關重要。本文探討了 Python 中的多執行緒機制,分析了其在提升 CPU 計算能力方面的優勢與侷限。雖然執行緒提供了一種便捷的方式實作並發執行,但也存在一些挑戰,例如 CPython 直譯器中的全域性直譯器鎖(GIL)限制了真正的平行計算,以及多執行緒程式設計本身帶來的同步和競爭條件等複雜性。

相較於其他程式語言,Python 的執行緒模型在 CPU 密集型任務中可能並非最佳解決方案。然而,對於 I/O 密集型應用,多執行緒仍然可以有效提升系統吞吐量。開發者需要仔細評估應用程式的特性,選擇合適的並發模型。例如,對於 CPU 密集型任務,使用多程式或其他技術可能更有效率。

展望未來,隨著 Python 生態系統的發展,新的並發模型和工具將持續湧現。開發者需要持續關注這些新技術,並根據實際需求調整技術策略。預計 Python 未來版本將會針對 GIL 進行最佳化,或者出現更多繞過 GIL 限制的方案,進一步提升 Python 在多核心環境下的效能表現。對於追求極致效能的應用,可以考慮使用 Cython 或其他編譯型語言來實作關鍵程式碼段,以充分發揮多核心 CPU 的優勢。

因此,玄貓建議開發者在應用多執行緒時,務必深入理解其工作原理和限制,並結合實際應用場景進行效能測試和調優,才能真正發揮多執行緒的優勢,避免不必要的效能損失。