Python - 線程池

哈囉,有志於 Python 程式設計的朋友們!今天,我們將深入探討線程池的迷人世界。作為您親切友善的電腦教師,我將逐步引導您走過這段旅程。如果您是程式設計新手,也請不用擔心;我們將從基礎開始,逐步進階。所以,拿起您最喜歡的飲料,放鬆身心,讓我們開始這次冒險吧!

Python - Thread Pools

線程池是什麼?

在我們進入代碼之前,先來了解線程池是什麼以及為何它如此重要。想像您正在經營一家忙碌的餐廳。當顧客走進來時,您不是每次都招聘新員工,而是已經有一支服務員團隊隨時待命。這支團隊就是您的「工作池」。在程式設計中,線程池類似於此 - 它是一組可重用的線程,當需要時即可進行工作。

線程池能夠在我們不為每個任務創建新線程的情況下,有效地管理多個任務。當您有許多短期任務需要同時執行時,它們特別有用。

現在,讓我們探討在 Python 中實現線程池的兩種主要方法:ThreadPool 類和 ThreadPoolExecutor 類。

使用 Python ThreadPool 類

ThreadPool 類是 multiprocessing.pool 模塊的一部分。它有點老舊,但仍然被廣泛使用。讓我們看看如何使用它:

from multiprocessing.pool import ThreadPool
import time

def worker(num):
print(f"Worker {num} is starting")
time.sleep(2)  # 模擬一些工作
print(f"Worker {num} is done")
return num * 2

# 創建一個具有 3 個工作線程的線程池
pool = ThreadPool(3)

# 向池中提交 5 個任務
results = pool.map(worker, range(5))

# 關閉池並等待所有任務完成
pool.close()
pool.join()

print("All workers have finished")
print(f"Results: {results}")

讓我們來解析一下:

  1. 我們導入了 ThreadPooltime(用於模擬工作)。
  2. 我們定義了一個 worker 函數,該函數模擬一些工作並返回一個值。
  3. 我們創建了一個具有 3 個工作線程的 ThreadPool
  4. 我們使用 pool.map() 將 5 個任務提交到池中。這將任務分配給可用的線程。
  5. 我們關閉池並等待所有任務完成。
  6. 最後,我們列印出結果。

當您運行此代碼時,您會看到即使我們有 5 個任務,它們也是由 3 個工作線程執行的,這展示了線程池如何管理工作負載。

使用 Python ThreadPoolExecutor 類

現在,讓我們看看來自 concurrent.futures 模塊的更現代的 ThreadPoolExecutor 類。這個類為異步執行可調用對象提供了更高級的接口。

from concurrent.futures import ThreadPoolExecutor
import time

def worker(num):
print(f"Worker {num} is starting")
time.sleep(2)  # 模擬一些工作
print(f"Worker {num} is done")
return num * 2

# 創建一個具有 3 個工作線程的 ThreadPoolExecutor
with ThreadPoolExecutor(max_workers=3) as executor:
# 向執行器提交 5 個任務
futures = [executor.submit(worker, i) for i in range(5)]

# 等待所有任務完成並獲取結果
results = [future.result() for future in futures]

print("All workers have finished")
print(f"Results: {results}")

讓我們解析這個例子:

  1. 我們導入了 ThreadPoolExecutor 代替 ThreadPool
  2. 我們使用 with 語句來創建和管理執行器。這有助於在我們完成時進行正確的清理。
  3. 我們使用 executor.submit() 將個別任務提交到池中。
  4. 我們創建了一個 Future 對象列表,這些對象代表了我們任務的最終結果。
  5. 我們使用 future.result() 來等待並检索每個任務的結果。

ThreadPoolExecutor 提供了更多的靈活性,通常更容易使用,特別是對於更複雜的情況。

比較 ThreadPool 和 ThreadPoolExecutor

讓我們來比較這兩種方法:

特性 ThreadPool ThreadPoolExecutor
模塊 multiprocessing.pool concurrent.futures
Python 版本 所有版本 3.2 及以後版本
上下文管理器
靈活性 较少 較多
錯誤處理 基本功能 高級功能
取消 有限支持 支援
Future 對象

如您所見,ThreadPoolExecutor 提供了更多的功能,並且通常更具靈活性。但是,ThreadPool 還是有用的,特別是如果您正在使用舊版本的 Python 或需要與現有代碼保持兼容性的情況下。

最佳實踐和技巧

  1. 選擇正確的線程數量:線程太少可能無法充分利用您的 CPU,而線程太多可能會導致開銷過大。一個好的起點是您的機器上的 CPU 核心數量。

  2. 使用上下文管理器:使用 ThreadPoolExecutor 時,請始終使用 with 語句以確保正確的清理。

  3. 處理異常:確保在您的 worker 函數中處理異常,以防止無聲失敗。

  4. 當心共享資源:使用線程池時,請謹慎處理共享資源以避免競態條件。

  5. 考慮任務細粒度:線程池在處理許多小任務時效果最佳,而不是幾個大任務。

結論

恭喜您!您已經踏出了在 Python 中使用線程池的第一步。我們已經涵蓋了 ThreadPoolThreadPoolExecutor 的基礎知識,現在您應該已經有了良好的基礎,可以開始在您自己的項目中使用這些強大的工具。

記住,就像在忙碌的餐廳廚房學習烹飪一樣,掌握線程池需要練習。不要害怕實驗和犯錯 - 這就是我們學習的方式!堅持編程,堅持學習,在您察覺之前,您將能像忙碌廚房中的專業廚師一樣熟练地操控線程。

編程愉快,願您的線程永遠和諧!

Credits: Image by storyset