淺談一下python線(xiàn)程池簡(jiǎn)單應(yīng)用
一、線(xiàn)程池簡(jiǎn)介
傳統(tǒng)多線(xiàn)程方案會(huì)使用“即時(shí)創(chuàng)建,即時(shí)銷(xiāo)毀”的策略。盡管與創(chuàng)建進(jìn)程相比,創(chuàng)建線(xiàn)程的時(shí)間已經(jīng)大大的縮短,但是如果提交給線(xiàn)程的任務(wù)時(shí)執(zhí)行時(shí)間較短,而且執(zhí)行次數(shù)及其頻繁,那么服務(wù)器將處于不停的創(chuàng)建線(xiàn)程,銷(xiāo)毀線(xiàn)程的狀態(tài)。
一個(gè)線(xiàn)程的運(yùn)行時(shí)間可以分為三部分:線(xiàn)程的啟動(dòng)時(shí)間、線(xiàn)程體的運(yùn)行時(shí)間和線(xiàn)程的銷(xiāo)毀時(shí)間。
在多線(xiàn)程處理的情景中,如果線(xiàn)程不能被重用,就意味著每次線(xiàn)程運(yùn)行都要經(jīng)過(guò)啟動(dòng)、銷(xiāo)毀和運(yùn)行3個(gè)過(guò)程。這必然會(huì)增加系統(tǒng)相應(yīng)的時(shí)間,減低了效率。
線(xiàn)程池在系統(tǒng)啟動(dòng)時(shí)即創(chuàng)建大量空閑的線(xiàn)程,程序只要將一個(gè)函數(shù)提交給線(xiàn)程池,線(xiàn)程池就會(huì)啟動(dòng)一個(gè)空閑的線(xiàn)程來(lái)執(zhí)行它。
當(dāng)該函數(shù)執(zhí)行結(jié)束后,該線(xiàn)程并不會(huì)死亡,而是再次返回到線(xiàn)程池中變成空閑狀態(tài),等待執(zhí)行下一個(gè)函數(shù),因此能夠避免多次創(chuàng)建線(xiàn)程,從而節(jié)省線(xiàn)程創(chuàng)建和銷(xiāo)毀的開(kāi)銷(xiāo),能帶來(lái)更好的性能和穩(wěn)定性。
此外,使用線(xiàn)程池可以有效地控制系統(tǒng)中并發(fā)線(xiàn)程的數(shù)量。當(dāng)系統(tǒng)中包含有大量的并發(fā)線(xiàn)程時(shí),會(huì)導(dǎo)致系統(tǒng)性能急劇下降,甚至導(dǎo)致Python解釋器崩潰,而線(xiàn)程池的最大線(xiàn)程數(shù)參數(shù)可以控制系統(tǒng)中并發(fā)線(xiàn)程的數(shù)量不超過(guò)此數(shù)。
服務(wù)器CPU數(shù)有限,能夠同時(shí)并發(fā)的線(xiàn)程數(shù)有限,并不是開(kāi)得越多越好,以及線(xiàn)程切換時(shí)有開(kāi)銷(xiāo)的,如果線(xiàn)程切換過(guò)于頻繁,反而會(huì)使性能降低。
線(xiàn)程池適用于:突發(fā)性大量請(qǐng)求或需要大量線(xiàn)程完成任務(wù),但實(shí)際任務(wù)處理時(shí)間較短的場(chǎng)景
二、線(xiàn)程池在python中的應(yīng)用
從python3.2開(kāi)始,標(biāo)準(zhǔn)庫(kù)提供了concurrent.futures模塊,它提供了兩個(gè)子類(lèi):ThreadPoolExecutor和ProcessPoolExecutor。其中ThreadPoolExecutor用于創(chuàng)建線(xiàn)程池,而ProcessPoolExecutor用于創(chuàng)建進(jìn)程池。不僅可以自動(dòng)調(diào)度線(xiàn)程,還可以做到:
- 主線(xiàn)程可以獲取某一個(gè)線(xiàn)程(或任務(wù))的狀態(tài),以及返回值
- 當(dāng)一個(gè)線(xiàn)程完成的時(shí)候,主線(xiàn)程能夠立即知道
- 讓多線(xiàn)程和多進(jìn)程編碼接口一致
使用線(xiàn)程池/進(jìn)程池來(lái)管理并發(fā)編程,只要將相應(yīng)的 task 函數(shù)提交給線(xiàn)程池/進(jìn)程池,剩下的事情就由線(xiàn)程池/進(jìn)程池來(lái)搞定。
ThreadPoolExecutor構(gòu)造函數(shù)有兩個(gè)參數(shù):
一個(gè)是max_workers參數(shù),用于指定線(xiàn)程池的最大線(xiàn)程數(shù),如果不指定的話(huà)則默認(rèn)是CPU核數(shù)的5倍。
另一個(gè)參數(shù)是thread_name_prefix,它用來(lái)指定線(xiàn)程池中線(xiàn)程的名稱(chēng)前綴(可選),如下:
threadPool = ThreadPoolExecutor(max_workers=self.max_workers, thread_name_prefix="test_")
Exectuor 提供了如下常用方法:
方法 | 描述 |
submit(fn, *args, **kwargs) | 將 fn 函數(shù)提交給線(xiàn)程池。*args 代表傳給 fn 函數(shù)的參數(shù),**kwargs 代表以關(guān)鍵字參數(shù)的形式為 fn 函數(shù)傳入?yún)?shù) |
map(func,*iterables, timeout=None, chunksize=1) | 該函數(shù)類(lèi)似于全局函數(shù) map(func, *iterables),只是該函數(shù)將會(huì)啟動(dòng)多個(gè)線(xiàn)程,以異步方式立即對(duì) iterables 執(zhí)行 map 處理 |
shutdown(wait=True) | 關(guān)閉線(xià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)給線(xiàn)程池后,submit 方法會(huì)返回一個(gè) Future 對(duì)象,F(xiàn)uture 類(lèi)主要用于獲取線(xiàn)程任務(wù)函數(shù)的返回值。由于線(xiàn)程任務(wù)會(huì)在新線(xiàn)程中以異步方式執(zhí)行,因此線(xiàn)程執(zhí)行的函數(shù)相當(dāng)于一個(gè)“將來(lái)完成”的任務(wù),所以 Python 使用 Future 來(lái)代表。
Future 提供了如下方法:
方法 | 描述 |
cancel() | 取消該 Future 代表的線(xiàn)程任務(wù)。如果該任務(wù)正在執(zhí)行,不可取消,則該方法返回 False;否則,程序會(huì)取消該任務(wù),并返回 True |
cancelled() | 返回 Future 代表的線(xiàn)程任務(wù)是否被成功取消 |
running() | 如果該 Future 代表的線(xiàn)程任務(wù)正在執(zhí)行、不可被取消,該方法返回 True |
done() | 如果該 Funture 代表的線(xiàn)程任務(wù)被成功取消或執(zhí)行完成,則該方法返回 True |
result(timeout=None) | 獲取該 Future 代表的線(xiàn)程任務(wù)最后返回的結(jié)果。如果 Future 代表的線(xiàn)程任務(wù)還未完成,該方法將會(huì)阻塞當(dāng)前線(xiàn)程,其中 timeout 參數(shù)指定最多阻塞多少秒 |
exception(timeout=None) | 獲取該 Future 代表的線(xiàn)程任務(wù)所引發(fā)的異常。如果該任務(wù)成功完成,沒(méi)有異常,則該方法返回 None |
add_done_callback(fn) | 為該 Future 代表的線(xiàn)程任務(wù)注冊(cè)一個(gè)“回調(diào)函數(shù)”,當(dāng)該任務(wù)成功完成時(shí),程序會(huì)自動(dòng)觸發(fā)該 fn 函數(shù) |
線(xiàn)程池用完后,應(yīng)調(diào)用線(xiàn)程池的shutdown()方法,關(guān)閉線(xiàn)程池。也可使用with語(yǔ)句來(lái)管理線(xiàn)程池,可避免手動(dòng)關(guān)閉線(xiàn)程池
示例一(使用submit方式):
from concurrent.futures import ThreadPoolExecutor # 導(dǎo)入ThreadPoolExecutor模塊 import time max_workers = 5 t = [] t1 = time.time() # 作為線(xiàn)程任務(wù)的函數(shù) def task(x, y): return x + y threadPool = ThreadPoolExecutor(max_workers) # 創(chuàng)建最大線(xiàn)程數(shù)為max_workers的線(xiàn)程池 for i in range(20): # 循環(huán)向線(xiàn)程池中提交task任務(wù) future = threadPool.submit(task, i, i+1) t.append(future) # 若不需要獲取返回值,則可不需要下面兩行代碼 for i in t: print(i.result()) # 獲取每個(gè)任務(wù)的返回值,result()會(huì)阻塞主線(xiàn)程 threadPool.shutdown() # 阻塞主線(xiàn)程,所有任務(wù)執(zhí)行完后關(guān)閉線(xiàn)程池 print(time.time() - t1)
示例二(使用map方式):
from concurrent.futures import ThreadPoolExecutor # 導(dǎo)入ThreadPoolExecutor模塊 max_workers = 5 t = [] t1 = time.time() # 作為線(xiàn)程任務(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)建最大線(xiàn)程數(shù)為max_workers的線(xiàn)程池 results = threadPool.map(task, args) # 啟動(dòng)線(xiàn)程,并收集每個(gè)線(xiàn)任務(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ì)先通知主線(xiàn)程
from concurrent.futures import ThreadPoolExecutor, as_completed import time max_workers = 5 t = [] t1 = time.time() # 作為線(xiàn)程任務(wù)的函數(shù) def task(x, y): return x + y def handle_result(future): print(future.result()) with ThreadPoolExecutor(max_workers) as threadPool: # 創(chuàng)建最大線(xiàn)程數(shù)為max_workers的線(xiàn)程池 for i in range(20): # 循環(huán)向線(xiàn)程池中提交task任務(wù) future = threadPool.submit(task, i, i+1) t.append(future) # 若不需要獲取返回值,則可不需要下面兩行代碼 for future in as_completed(t): # as_completed,哪個(gè)先完成就先處理哪個(gè),會(huì)阻塞主線(xiàn)程,直到完成所有,除非設(shè)置timeout future.add_done_callback(handle_result)
到此這篇關(guān)于淺談一下python線(xiàn)程池簡(jiǎn)單應(yīng)用的文章就介紹到這了,更多相關(guān)python線(xiàn)程池簡(jiǎn)單應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python3 導(dǎo)入上級(jí)目錄中的模塊實(shí)例
今天小編就為大家分享一篇Python3 導(dǎo)入上級(jí)目錄中的模塊實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-02-02Python for循環(huán)與getitem的關(guān)系詳解
這篇文章主要介紹了Python for循環(huán)與getitem的關(guān)系詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01解決pytorch 損失函數(shù)中輸入輸出不匹配的問(wèn)題
這篇文章主要介紹了解決pytorch 損失函數(shù)中輸入輸出不匹配的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06安裝Python和pygame及相應(yīng)的環(huán)境變量配置(圖文教程)
下面小編就為大家?guī)?lái)一篇安裝Python和pygame及相應(yīng)的環(huán)境變量配置(圖文教程)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06使用Python畫(huà)出小人發(fā)射愛(ài)心的代碼
今天小編就為大家分享一篇使用Python畫(huà)出小人發(fā)射愛(ài)心的代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11深入理解Python內(nèi)置函數(shù)eval的使用
在Python中,eval函數(shù)是一個(gè)內(nèi)置函數(shù),用于將字符串解析并執(zhí)行為Python表達(dá)式,本文將詳細(xì)介紹eval函數(shù)的使用方法和注意事項(xiàng),需要的可以參考一下2023-06-06