一、為什么要使用執行緒池
多執行緒的情況下確實可以最大限度發揮多核處理器的計算能力,提高系統的吞吐量和性能,
但是如果隨意使用多執行緒,對系統的性能反而有不利影響,
比如下面的情況:
創建執行緒是需要時間的,假設執行緒創建所需時間為T1,執行緒執行任務時間為T2,執行緒銷毀時間為T3,而往往T1+T3>T2,所以頻繁創建和銷毀執行緒也會消耗大量的時間,
如果有任務來了,再去創建執行緒的話效率比較低,
其次執行緒也需要占用記憶體空間,執行緒池可以管理控制執行緒,執行緒是稀缺資源,如果無休止的創建會消耗系統資源,還會降低系統穩定性,
因此,為了避免頻繁的創建和銷毀執行緒,讓創建的執行緒進行復用,就有了執行緒池的概念,
使用執行緒池可以進行統一分配,方便調優和監控,
執行緒池里會維護一部分活躍執行緒,如果有需要,就去執行緒池里取執行緒使用,用完即歸還到執行緒池里,免去了創建和銷毀執行緒的開銷,且執行緒池也會執行緒的數量有一定的限制,
二、執行緒池的使用
執行緒池的基類是 concurrent.futures 模塊中的 Executor,
Executor 提供了兩個子類,即 ThreadPoolExecutor 和 ProcessPoolExecutor,
其中 ThreadPoolExecutor 用于創建執行緒池,而 ProcessPoolExecutor 用于創建行程池,
如果使用執行緒池/行程池來管理并發編程,那么只要將相應的 task 函式提交給執行緒池/行程池,剩下的事情就由執行緒池/行程池來搞定,
Exectuor 提供了如下常用方法:
- submit
submit(fn, *args, **kwargs)
submit 將 fn 函式提交給執行緒池,
*args 代表傳給 fn 函式的引數,
*kwargs 代表以關鍵字引數的形式為 fn 函式傳入引數,
- map
map(func, *iterables, timeout=None, chunksize=1)
map 函式類似于全域函式 map(func, *iterables),只是該函式將會啟動多個執行緒,以異步方式立即對 iterables 執行 map 處理,
- 關閉執行緒池,
shutdown(wait=True)
程式將函式提交(submit)給執行緒池后,submit 方法會回傳一個物件,該物件主要用于獲取執行緒任務函式的回傳值,
該物件提供了如下方法:
- cancel():取消該執行緒任務,如果該任務正在執行,不可取消,則該方法回傳 False;否則,程式會取消該任務,并回傳 True,
- cancelled():回傳執行緒任務是否被成功取消,
- running():如果該執行緒任務正在執行、不可被取消,該方法回傳 True,
- done():如果該執行緒任務被成功取消或執行完成,則該方法回傳 True,
- result(timeout=None):獲取該執行緒任務最后回傳的結果,如果該執行緒任務還未完成,該方法將會阻塞當前執行緒,其中 timeout 引數指定最多阻塞多少秒,
- exception(timeout=None):獲取該執行緒任務所引發的例外,如果該任務成功完成,沒有例外,則該方法回傳 None,
- add_done_callback(fn):為該執行緒任務注冊一個“回呼函式”,當該任務成功完成時,程式會自動觸發該 fn 函式,
在用完一個執行緒池后,應該呼叫該執行緒池的 shutdown() 方法,該方法將啟動執行緒池的關閉序列,
呼叫 shutdown() 方法后的執行緒池不再接收新任務,但會將以前所有的已提交任務執行完成,
當執行緒池中的所有任務都執行完成后,該執行緒池中的所有執行緒都會死亡,
使用執行緒池來執行執行緒任務的步驟
- 呼叫 ThreadPoolExecutor 類的構造器創建一個執行緒池,
- 定義一個普通函式作為執行緒任務,
- 呼叫 ThreadPoolExecutor 物件的 submit() 方法來提交執行緒任務,
- 當不想提交任何任務時,呼叫 ThreadPoolExecutor 物件的 shutdown() 方法來關閉執行緒池,
from concurrent.futures import ThreadPoolExecutor
import threading
import time
def action(max):
sum = 0
for i in range(max):
print(threading.current_thread().name+' '+str(i))
sum += i
return sum
# 創建一個包含2個執行緒的執行緒池
pool = ThreadPoolExecutor(max_workers=2)
# 向執行緒池提交一個任務
future1 = pool.submit(action, 3)
# 向執行緒池再提交一個任務
future2 = pool.submit(action, 5)
# 判斷 future1 的任務是否結束
print(future1.done())
time.sleep(2)
# 判斷 future2 的任務是否結束
print(future2.done())
# 查看future1代表的任務回傳的結果
print(future1.result())
# 查看future2代表的任務回傳的結果
print(future2.result())
# 關閉執行緒池
pool.shutdown()
運行結果:
ThreadPoolExecutor-0_0 0
ThreadPoolExecutor-0_0 1
ThreadPoolExecutor-0_1 0
ThreadPoolExecutor-0_1 1
ThreadPoolExecutor-0_1 2
ThreadPoolExecutor-0_1 3
ThreadPoolExecutor-0_1 4
False
ThreadPoolExecutor-0_0 2
True
3
10
總結:
執行緒池在系統啟動時即創建一些空閑的執行緒,程式只要將一個函式提交給執行緒池,執行緒池就會啟動一個空閑的執行緒來執行它,
當該函式執行結束后,該執行緒并不會死亡,而是再次回傳到執行緒池中變成空閑狀態,等待執行下一個函式,
此外,使用執行緒池可以有效地控制系統中并發執行緒的數量,當系統中包含有大量的并發執行緒時,會導致系統性能急劇下降,甚至導致 Python 解釋器崩潰,而執行緒池的最大執行緒數引數可以控制系統中并發執行緒的數量不超過此數,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/141556.html
標籤:Python
