溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

python線程池怎么應(yīng)用

發(fā)布時(shí)間:2023-04-19 15:53:32 來(lái)源:億速云 閱讀:106 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹了python線程池怎么應(yīng)用的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇python線程池怎么應(yīng)用文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。

一、線程池簡(jiǎn)介

傳統(tǒng)多線程方案會(huì)使用“即時(shí)創(chuàng)建,即時(shí)銷毀”的策略。盡管與創(chuàng)建進(jìn)程相比,創(chuàng)建線程的時(shí)間已經(jīng)大大的縮短,但是如果提交給線程的任務(wù)時(shí)執(zhí)行時(shí)間較短,而且執(zhí)行次數(shù)及其頻繁,那么服務(wù)器將處于不停的創(chuàng)建線程,銷毀線程的狀態(tài)。

一個(gè)線程的運(yùn)行時(shí)間可以分為三部分:線程的啟動(dòng)時(shí)間、線程體的運(yùn)行時(shí)間和線程的銷毀時(shí)間。

在多線程處理的情景中,如果線程不能被重用,就意味著每次線程運(yùn)行都要經(jīng)過(guò)啟動(dòng)、銷毀和運(yùn)行3個(gè)過(guò)程。這必然會(huì)增加系統(tǒng)相應(yīng)的時(shí)間,減低了效率。

線程池在系統(tǒng)啟動(dòng)時(shí)即創(chuàng)建大量空閑的線程,程序只要將一個(gè)函數(shù)提交給線程池,線程池就會(huì)啟動(dòng)一個(gè)空閑的線程來(lái)執(zhí)行它。

當(dāng)該函數(shù)執(zhí)行結(jié)束后,該線程并不會(huì)死亡,而是再次返回到線程池中變成空閑狀態(tài),等待執(zhí)行下一個(gè)函數(shù),因此能夠避免多次創(chuàng)建線程,從而節(jié)省線程創(chuàng)建和銷毀的開(kāi)銷,能帶來(lái)更好的性能和穩(wěn)定性。

此外,使用線程池可以有效地控制系統(tǒng)中并發(fā)線程的數(shù)量。當(dāng)系統(tǒng)中包含有大量的并發(fā)線程時(shí),會(huì)導(dǎo)致系統(tǒng)性能急劇下降,甚至導(dǎo)致Python解釋器崩潰,而線程池的最大線程數(shù)參數(shù)可以控制系統(tǒng)中并發(fā)線程的數(shù)量不超過(guò)此數(shù)。

服務(wù)器CPU數(shù)有限,能夠同時(shí)并發(fā)的線程數(shù)有限,并不是開(kāi)得越多越好,以及線程切換時(shí)有開(kāi)銷的,如果線程切換過(guò)于頻繁,反而會(huì)使性能降低。

線程池適用于:突發(fā)性大量請(qǐng)求或需要大量線程完成任務(wù),但實(shí)際任務(wù)處理時(shí)間較短的場(chǎng)景

二、線程池在python中的應(yīng)用

從python3.2開(kāi)始,標(biāo)準(zhǔn)庫(kù)提供了concurrent.futures模塊,它提供了兩個(gè)子類:ThreadPoolExecutor和ProcessPoolExecutor。其中ThreadPoolExecutor用于創(chuàng)建線程池,而ProcessPoolExecutor用于創(chuàng)建進(jìn)程池。不僅可以自動(dòng)調(diào)度線程,還可以做到:

  • 主線程可以獲取某一個(gè)線程(或任務(wù))的狀態(tài),以及返回值

  • 當(dāng)一個(gè)線程完成的時(shí)候,主線程能夠立即知道

  • 讓多線程和多進(jìn)程編碼接口一致

使用線程池/進(jìn)程池來(lái)管理并發(fā)編程,只要將相應(yīng)的 task 函數(shù)提交給線程池/進(jìn)程池,剩下的事情就由線程池/進(jìn)程池來(lái)搞定。

ThreadPoolExecutor構(gòu)造函數(shù)有兩個(gè)參數(shù):

一個(gè)是max_workers參數(shù),用于指定線程池的最大線程數(shù),如果不指定的話則默認(rèn)是CPU核數(shù)的5倍。

另一個(gè)參數(shù)是thread_name_prefix,它用來(lái)指定線程池中線程的名稱前綴(可選),如下:

threadPool = ThreadPoolExecutor(max_workers=self.max_workers, thread_name_prefix="test_")

Exectuor 提供了如下常用方法:

方法描述
submit(fn, *args, **kwargs)將 fn 函數(shù)提交給線程池。*args 代表傳給 fn 函數(shù)的參數(shù),**kwargs 代表以關(guān)鍵字參數(shù)的形式為 fn 函數(shù)傳入?yún)?shù)
map(func,*iterables, timeout=None, chunksize=1)該函數(shù)類似于全局函數(shù) map(func, *iterables),只是該函數(shù)將會(huì)啟動(dòng)多個(gè)線程,以異步方式立即對(duì) iterables 執(zhí)行 map 處理
shutdown(wait=True)關(guān)閉線程池。wait=True,等待池內(nèi)所有任務(wù)執(zhí)行完畢回收完資源后才繼續(xù);wait=False,立即返回,并不會(huì)等待池內(nèi)的任務(wù)執(zhí)行完畢。但不管wait參數(shù)為何值,整個(gè)程序都會(huì)等到所有任務(wù)執(zhí)行完畢

程序?qū)?task 函數(shù)提交(submit)給線程池后,submit 方法會(huì)返回一個(gè) Future 對(duì)象,F(xiàn)uture 類主要用于獲取線程任務(wù)函數(shù)的返回值。由于線程任務(wù)會(huì)在新線程中以異步方式執(zhí)行,因此線程執(zhí)行的函數(shù)相當(dāng)于一個(gè)“將來(lái)完成”的任務(wù),所以 Python 使用 Future 來(lái)代表。

Future 提供了如下方法:

方法描述
cancel()取消該 Future 代表的線程任務(wù)。如果該任務(wù)正在執(zhí)行,不可取消,則該方法返回 False;否則,程序會(huì)取消該任務(wù),并返回 True
cancelled()返回 Future 代表的線程任務(wù)是否被成功取消
running()如果該 Future 代表的線程任務(wù)正在執(zhí)行、不可被取消,該方法返回 True
done()如果該 Funture 代表的線程任務(wù)被成功取消或執(zhí)行完成,則該方法返回 True
result(timeout=None)獲取該 Future 代表的線程任務(wù)最后返回的結(jié)果。如果 Future 代表的線程任務(wù)還未完成,該方法將會(huì)阻塞當(dāng)前線程,其中 timeout 參數(shù)指定最多阻塞多少秒
exception(timeout=None)獲取該 Future 代表的線程任務(wù)所引發(fā)的異常。如果該任務(wù)成功完成,沒(méi)有異常,則該方法返回 None
add_done_callback(fn)為該 Future 代表的線程任務(wù)注冊(cè)一個(gè)“回調(diào)函數(shù)”,當(dāng)該任務(wù)成功完成時(shí),程序會(huì)自動(dòng)觸發(fā)該 fn 函數(shù)

線程池用完后,應(yīng)調(diào)用線程池的shutdown()方法,關(guān)閉線程池。也可使用with語(yǔ)句來(lái)管理線程池,可避免手動(dòng)關(guān)閉線程池

示例一(使用submit方式):

from concurrent.futures import ThreadPoolExecutor # 導(dǎo)入ThreadPoolExecutor模塊
import time
 
max_workers = 5
t = []
t1 = time.time()
 
# 作為線程任務(wù)的函數(shù)
def task(x, y):
    return x + y
 
threadPool = ThreadPoolExecutor(max_workers)    # 創(chuàng)建最大線程數(shù)為max_workers的線程池
 
for i in range(20):     # 循環(huán)向線程池中提交task任務(wù)
    future = threadPool.submit(task, i, i+1)
    t.append(future)
 
# 若不需要獲取返回值,則可不需要下面兩行代碼
for i in t:
    print(i.result())   # 獲取每個(gè)任務(wù)的返回值,result()會(huì)阻塞主線程
 
threadPool.shutdown()   # 阻塞主線程,所有任務(wù)執(zhí)行完后關(guān)閉線程池
print(time.time() - t1)

示例二(使用map方式):

from concurrent.futures import ThreadPoolExecutor	# 導(dǎo)入ThreadPoolExecutor模塊
 
max_workers = 5
t = []
t1 = time.time()
 
# 作為線程任務(wù)的函數(shù)
def task(x):
    return x + (x + 1)
 
args = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
 
with ThreadPoolExecutor(max_workers) as threadPool:   # 創(chuàng)建最大線程數(shù)為max_workers的線程池
    results = threadPool.map(task, args)    # 啟動(dòng)線程,并收集每個(gè)線任務(wù)的返回結(jié)果
 
    # 若無(wú)返回值,則可不需要下面兩行代碼
    for i in results:
        print(i)

示例三:

as_complete():是一個(gè)生成器,在沒(méi)有任務(wù)完成的時(shí)候會(huì)阻塞,在有某個(gè)任務(wù)完成的時(shí)候會(huì)yield這個(gè)任務(wù),執(zhí)行語(yǔ)句,繼續(xù)阻塞,循環(huán)到所有任務(wù)結(jié)束,先完成的任務(wù)會(huì)先通知主線程

from concurrent.futures import ThreadPoolExecutor, as_completed
import time
 
max_workers = 5
t = []
t1 = time.time()
 
# 作為線程任務(wù)的函數(shù)
def task(x, y):
    return x + y
 
def handle_result(future):
    print(future.result())
 
with ThreadPoolExecutor(max_workers) as threadPool:    # 創(chuàng)建最大線程數(shù)為max_workers的線程池
 
    for i in range(20):     # 循環(huán)向線程池中提交task任務(wù)
        future = threadPool.submit(task, i, i+1)
        t.append(future)
 
    # 若不需要獲取返回值,則可不需要下面兩行代碼
    for future in as_completed(t):  # as_completed,哪個(gè)先完成就先處理哪個(gè),會(huì)阻塞主線程,直到完成所有,除非設(shè)置timeout
        future.add_done_callback(handle_result)

關(guān)于“python線程池怎么應(yīng)用”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“python線程池怎么應(yīng)用”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI