1. 何時使用線程池
系統(tǒng)啟動一個新線程的成本是比較高的,因為它涉及與操作系統(tǒng)的交互。在這種情形下,使用線程池可以很好地提升性能;
尤其是當(dāng)程序中需要創(chuàng)建大量生存期很短暫的線程時,更應(yīng)該考慮使用線程池。
線程池在系統(tǒng)啟動時即創(chuàng)建大量空閑的線程,程序只要將一個函數(shù)提交給線程池,線程池就會啟動一個空閑的線程來執(zhí)行它。
當(dāng)該函數(shù)執(zhí)行結(jié)束后,該線程并不會死亡,而是再次返回到線程池中變成空閑狀態(tài),等待執(zhí)行下一個函數(shù)。
使用線程池可以有效地控制系統(tǒng)中并發(fā)線程的數(shù)量。當(dāng)系統(tǒng)中包含有大量的并發(fā)線程時,會導(dǎo)致系統(tǒng)性能急劇下降,甚至導(dǎo)致 Python 解釋器崩潰,而線程池的最大線程數(shù)參數(shù)可以控制系統(tǒng)中并發(fā)線程的數(shù)量不超過此數(shù)。
2. 線程池
線程池的基類是concurrent.futures模塊中的Executor, Executor提供了兩個子類,即ThreadPoolExecutor 和 ProcessPoolExecutor
ThreadPoolExecutor?? ??? ?線程池
ProcessPoolExecutor?? ??? ?進程池
使用線程池/進程池來管理并發(fā)編程,那么只要將相應(yīng)的task函數(shù)提交給線程池/進程池,剩下的就由線程池/進程池來做
常用方法:
submit(fn, *args, **kwargs)
fn 函數(shù)?
*args ? ? ?傳給fn函數(shù)的參數(shù)
*kargs ? ? 以關(guān)鍵字參數(shù)的形式傳給fn函數(shù)的參數(shù)
map(func, *iterables, timeout = None, chunksize = 1)
類似全局函數(shù)map(func, *iterables),只是該函數(shù)將會啟動多個線程,以異步方式立即對iterables執(zhí)行map處理
shutdown(wait=True)
程序?qū)ask函數(shù)提交給線程池后,submit方法會返回一個Future對象,F(xiàn)uture類主要用于獲取線程任務(wù)函數(shù)的返回值。
由于線程任務(wù)會在新線程中以異步方式執(zhí)行,因此,線程執(zhí)行的函數(shù)相當(dāng)于一個“將來完成”的任務(wù),所以使用Future來代表
Future提供了以下方法:
cancel():?? ??? ??? ?取消該Future代表的線程任務(wù)。如果該任務(wù)正在執(zhí)行,不可取消,則該方法返回False;否則,程序會取消該任務(wù),并返回True。
cancelled(): ?? ?返回Future代表的線程任務(wù)是否被成功取消
running():?? ??? ?如果該Future代表的線程任務(wù)正在執(zhí)行、不可被取消,該方法返回True。
done():?? ??? ??? ??? ?如果該Future代表的線程任務(wù)被成功取消或這行完成,則該方法返回True。
result(timeout=None):?? ?獲取該Future代表的線程任務(wù)最后返回的結(jié)果。如果Future代表的線程任務(wù)還未能完成,該方法將會阻塞當(dāng)前線程,其中timeout參數(shù)指定最多阻塞多少秒
exception(timeout=None): ?? ?獲取該Future代表的線程任務(wù)所引發(fā)的異常,如果該任務(wù)成功完成,沒有異常,則該方法返回None
add_done_callback(fn):?? ??? ?為該Future代表的線程任務(wù)注冊一個“回調(diào)函數(shù)”,當(dāng)該任務(wù)成功完成時,程序會自動觸發(fā)該fn函數(shù)
在用完一個線程池后,應(yīng)該調(diào)用該線程池的shutdown()方法,該方法將啟動線程池的關(guān)閉序列。
調(diào)用shutdown()方法后的線程池不再接受新任務(wù),但會將以前所有的已提交的任務(wù)執(zhí)行完成。當(dāng)線程池中的所有任務(wù)都執(zhí)行完成后,該線程池中的所有線程都會死亡。
示例:
"""
使用線程池來執(zhí)行線程任務(wù):
1. 調(diào)用ThreadPoolExecutor類的構(gòu)造器創(chuàng)建一個線程池
2. 定義一個普通函數(shù)作為線程任務(wù)
3. 調(diào)用ThreadPoolExecutor對象的submit()方法來提交線程任務(wù)
4. 當(dāng)不想提交任何任務(wù)時,調(diào)用ThreadPoolExecutor對象的shutdown()方法來關(guān)閉線程池
"""
from concurrent.futures import ThreadPoolExecutor
import threading
import time
#線程任務(wù)
def task(max):
? ? my_sum = 0
? ? for i in range(max):
? ? ? ? print(threading.current_thread().name + ' ' + str(i))
? ? ? ? my_sum += i
? ? return my_sum
#創(chuàng)建一個包含2條線程的線程池
pool = ThreadPoolExecutor(max_workers = 2)
#向線程池提交一個task,
future1 = pool.submit(task, 50)
#向線程池再提交一個task
future2 = pool.submit(task, 100)
#判斷future1代表的任務(wù)是否結(jié)束
print(future1.done())
time.sleep(3)
#判斷future2代表的任務(wù)是否結(jié)束
print(future2.done())
#查看future1代表的任務(wù)返回結(jié)果
print(future1.result()) ? ? ? ? ? ? #會阻塞主線程在此等待
#查看future2代表的任務(wù)返回結(jié)果
print(future2.result())
#如果想不阻塞主線程,可以使用add_done_callback方法
#示例:
#def callback_get_result(future):
# ? future.result()
#
#future1.add_done_callback(callback_get_result)
#future2.add_done_callback(callback_get_result)
#關(guān)閉線程池
pool.shutdown()
由于線程池實現(xiàn)了上下文管理協(xié)議(Context Manage Protocol),因此,程序可以使用with語句來管理線程池,這樣即可避免手動關(guān)閉線程池
Executor提供了一個map(func, *iterables, timeout=None, chunksize=1)方法
類似于全局函數(shù)map()
區(qū)別在于線程池的map()會為iterables的每個元素啟動一個線程,已并發(fā)方式執(zhí)行func函數(shù)。這種方式相當(dāng)于啟動len(iterables)個線程,并收集每個線程的執(zhí)行結(jié)果。
示例:
with ThreadPoolExecutor(max_workers=4) as pool:
?? ?#使用map,元組中有3個元素,因此程序啟動3條線程來執(zhí)行task
?? ?results = pool.map(task, (50,100,250))?? ?
?? ?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
