Python探索之自定義實(shí)現(xiàn)線程池
為什么需要線程池呢?
設(shè)想一下,如果我們使用有任務(wù)就開啟一個(gè)子線程處理,處理完成后,銷毀子線程或等得子線程自然死亡,那么如果我們的任務(wù)所需時(shí)間比較短,但是任務(wù)數(shù)量比較多,那么更多的時(shí)間是花在線程的創(chuàng)建和結(jié)束上面,效率肯定就低了。
線程池的原理:
既然是線程池(Thread pool),其實(shí)名字很形象,就是把指定數(shù)量的可用子線程放進(jìn)一個(gè)"池里",有任務(wù)時(shí)取出一個(gè)線程執(zhí)行,任務(wù)執(zhí)行完后,并不立即銷毀線程,而是放進(jìn)線程池中,等待接收下一個(gè)任務(wù)。這樣內(nèi)存和cpu的開銷也比較小,并且我們可以控制線程的數(shù)量。
線程池的實(shí)現(xiàn):
線程池有很多種實(shí)現(xiàn)方式,在python中,已經(jīng)給我們提供了一個(gè)很好的實(shí)現(xiàn)方式:Queue-隊(duì)列。因?yàn)閜ython中Queue本身就是同步的,所以也就是線程安全的,所以我們可以放心的讓多個(gè)線程共享一個(gè)Queue。
那么說(shuō)到線程池,那么理應(yīng)也得有一個(gè)任務(wù)池,任務(wù)池中存放著待執(zhí)行的任務(wù),各個(gè)線程到任務(wù)池中取任務(wù)執(zhí)行,那么用Queue來(lái)實(shí)現(xiàn)任務(wù)池是最好不過的。
1.low版線程池
設(shè)計(jì)思路:運(yùn)用隊(duì)列queue
將線程類名放入隊(duì)列中,執(zhí)行一個(gè)就拿一個(gè)出來(lái)
import queue import threading class ThreadPool(object): def __init__(self, max_num=20): self.queue = queue.Queue(max_num) #創(chuàng)建隊(duì)列,最大數(shù)為20 for i in range(max_num): self.queue.put(threading.Thread) #將類名放入隊(duì)列中 def get_thread(self): return self.queue.get() #從隊(duì)列中取出類名 def add_thread(self): self.queue.put(threading.Thread) #進(jìn)類名放入隊(duì)列中 def func(arg, p): #定義一個(gè)函數(shù) print(arg) import time time.sleep(2) p.add_thread() pool = ThreadPool(10) #創(chuàng)建對(duì)象,并執(zhí)行該類的構(gòu)造方法,即將線程的類名放入隊(duì)列中 for i in range(30): thread = pool.get_thread() #調(diào)用該對(duì)象的get_thread方法,取出類名 t = thread(target=func, args=(i, pool)) #創(chuàng)建對(duì)象,執(zhí)行func,參數(shù)在args中 t.start()
由于此方法要求使用者修改原函數(shù),并在原函數(shù)里傳參數(shù),且調(diào)用方法也發(fā)生了改變,并且有空閑線程浪費(fèi)資源,實(shí)際操作中并不方便,故設(shè)計(jì)了下一版線程池。
2.絕版線程池
設(shè)計(jì)思路:運(yùn)用隊(duì)列queue
a.隊(duì)列里面放任務(wù)
b.線程一次次去取任務(wù),線程一空閑就去取任務(wù)
import queue import threading import contextlib import time StopEvent = object() class ThreadPool(object): def __init__(self, max_num, max_task_num = None): if max_task_num: self.q = queue.Queue(max_task_num) else: self.q = queue.Queue() self.max_num = max_num self.cancel = False self.terminal = False self.generate_list = [] self.free_list = [] def run(self, func, args, callback=None): """ 線程池執(zhí)行一個(gè)任務(wù) :param func: 任務(wù)函數(shù) :param args: 任務(wù)函數(shù)所需參數(shù) :param callback: 任務(wù)執(zhí)行失敗或成功后執(zhí)行的回調(diào)函數(shù),回調(diào)函數(shù)有兩個(gè)參數(shù)1、任務(wù)函數(shù)執(zhí)行狀態(tài);2、任務(wù)函數(shù)返回值(默認(rèn)為None,即:不執(zhí)行回調(diào)函數(shù)) :return: 如果線程池已經(jīng)終止,則返回True否則None """ if self.cancel: return if len(self.free_list) == 0 and len(self.generate_list) < self.max_num: self.generate_thread() w = (func, args, callback,) self.q.put(w) def generate_thread(self): """ 創(chuàng)建一個(gè)線程 """ t = threading.Thread(target=self.call) t.start() def call(self): """ 循環(huán)去獲取任務(wù)函數(shù)并執(zhí)行任務(wù)函數(shù) """ current_thread = threading.currentThread() self.generate_list.append(current_thread) event = self.q.get() while event != StopEvent: func, args, callback = event try: result = func(*args) success = True except Exception as e: success = False result = None if callback is not None: try: callback(success, result) except Exception as e: pass with self.worker_state(self.free_list, current_thread): if self.terminal: event = StopEvent else: event = self.q.get() else: self.generate_list.remove(current_thread) def close(self): """ 執(zhí)行完所有的任務(wù)后,所有線程停止 """ self.cancel = True count = len(self.generate_list) while count: self.q.put(StopEvent) count -= 1 def terminate(self): """ 無(wú)論是否還有任務(wù),終止線程 """ self.terminal = True while self.generate_list: self.q.put(StopEvent) self.q.queue.clear() @contextlib.contextmanager def worker_state(self, state_list, worker_thread): """ 用于記錄線程中正在等待的線程數(shù) """ state_list.append(worker_thread) try: yield finally: state_list.remove(worker_thread) # How to use pool = ThreadPool(5) def callback(status, result): # status, execute action status # result, execute action return value pass def action(i): print(i) for i in range(30): ret = pool.run(action, (i,), callback) time.sleep(3) print(len(pool.generate_list), len(pool.free_list)) print(len(pool.generate_list), len(pool.free_list)) pool.close() # pool.terminate()
總結(jié)
以上就是本文關(guān)于Python探索之自定義實(shí)現(xiàn)線程池的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:python中模塊的__all__屬性詳解、Python面向?qū)ο缶幊袒A(chǔ)解析(二)等,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
相關(guān)文章
基于PyQt5制作Excel數(shù)據(jù)分組匯總器
這篇文章主要介紹了基于PyQt5制作的一個(gè)小工具:Excel數(shù)據(jù)分組匯總器。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起試一試2022-01-01Python函數(shù)之iterrows(),iteritems(),itertuples()的區(qū)別說(shuō)明
這篇文章主要介紹了Python函數(shù)之iterrows(),iteritems(),itertuples()的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05python爬蟲基礎(chǔ)知識(shí)點(diǎn)整理
在本篇文章里小編給大家整理的是一篇關(guān)于python爬蟲基礎(chǔ)知識(shí)點(diǎn)整理內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。2020-06-06詳解基于Transformer實(shí)現(xiàn)電影評(píng)論星級(jí)分類任務(wù)
這篇文章主要為大家介紹了詳解基于Transformer實(shí)現(xiàn)電影評(píng)論星級(jí)分類任務(wù)過程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04使用python實(shí)現(xiàn)mqtt的發(fā)布和訂閱
這篇文章主要介紹了使用python實(shí)現(xiàn)mqtt的發(fā)布和訂閱,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-05-05python圖形用戶界面tkinter之按鈕Button的使用說(shuō)明
這篇文章主要介紹了python圖形用戶界面tkinter之按鈕Button的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06