Python多線程-線程池ThreadPoolExecutor
1. 線程池
為什么要使用線程池?
1)、多線程中, 線程的數(shù)量并非越多越好;
不是線程數(shù)量越多,程序的執(zhí)行效率就越快。線程也是一個對象,是需要占用資源的,線程數(shù)量過多的話肯定會消耗過多的資源,同時線程間的上下文切換也是一筆不小的開銷,所以有時候開辟過多的線程不但不會提高程序的執(zhí)行效率,反而會適得其反使程序變慢,得不償失。
2)、節(jié)省每次開啟線程的開銷。
為了防止無盡的線程被初始化,于是線程池就誕生了。當(dāng)線程池初始化時,會自動創(chuàng)建指定數(shù)量的線程,有任務(wù)到達(dá)時直接從線程池中取一個空閑線程來用即可,當(dāng)任務(wù)執(zhí)行結(jié)束時線程不會消亡而是直接進(jìn)入空閑狀態(tài),繼續(xù)等待下一個任務(wù)。而隨著任務(wù)的增加線程池中的可用線程必將逐漸減少,當(dāng)減少至零時,任務(wù)就需要等待了。這就最大程度的避免了線程的無限創(chuàng)建,當(dāng)所需要使用的線程不知道有多少時,一般都會使用線程池。
在 python 中使用線程池有兩種方式,一種是基于第三方庫 threadpool,另一種是基于 python3 新引入的庫 concurrent.futures.ThreadPoolExecutor,這里我們介紹一下后一種。
concurrent.futures.ThreadPoolExecutor,在提交任務(wù)的時候有兩種方式,一種是submit()函數(shù),另一種是map()函數(shù),兩者的主要區(qū)別在于:
1)、map可以保證輸出的順序, submit輸出的順序是亂的。
2)、如果你要提交的任務(wù)的函數(shù)是一樣的,就可以簡化成map。但是假如提交的任務(wù)函數(shù)是不一樣的,或者執(zhí)行的過程之可能出現(xiàn)異常(使用map執(zhí)行過程中發(fā)現(xiàn)問題會直接拋出錯誤)就要用到submit()。
3)、submit和map的參數(shù)是不同的,submit每次都需要提交一個目標(biāo)函數(shù)和對應(yīng)的參數(shù),map只需要提交一次目標(biāo)函數(shù),目標(biāo)函數(shù)的參數(shù)放在一個迭代器(列表,字典)里就可以。
2.submit方法
- ThreadPoolExecutor
ThreadPoolExecutor(max_workers=None, thread_name_prefix='', initializer=None, initargs=())
最常用的是 max_workers 參數(shù),即線程池中的線程數(shù)。
- submit
submit(fn, *args, **kwargs)
其中 fn 為方法名,其后的 *args, **kwargs 為該方法的參數(shù)。
- 示例:
import threading from concurrent.futures import ThreadPoolExecutor import time import random # 對兩數(shù)進(jìn)行加法,并停留0-60隨機秒數(shù) def add(a, b): sum = a + b slp = random.randint(0,60) time.sleep(slp) print('{} is running, {} + {} = {} ,thread sleep {} seconds'.format(threading.current_thread().name,a,b,sum,slp)) # 加法的兩個因數(shù) list_a = [1, 2, 3, 4, 5, 6 ,7] list_b = [6, 7, 8, 9, 10 ,11 ,12] # 使用with上下問管理器,就不用管如何關(guān)線程池了 with ThreadPoolExecutor(2) as pool: # 將每一個線程都進(jìn)行提交 for i in range(len(list_a)): pool.submit(add, list_a[i], list_b[i])
運行截圖:

3.map方法
- map :
map(func, *iterables, timeout=None, chunksize=1)
func 參數(shù)為多線程指向的方法名,*iterables 實際上是該方法的參數(shù),該方法的參數(shù)必須是可迭代對象,即元組或列表等,不能單純的傳遞 int 或字符串,如果 timeout 設(shè)定的時間小于線程執(zhí)行時間會拋異常 TimeoutError,默認(rèn)為 None 則不加限制。
使用 map 方法,有兩個特點:
無需提前使用submit方法
返回結(jié)果的順序和元素的順序相同,即使子線程先返回也不會獲取結(jié)果
- 示例:
import time import random import threading from concurrent.futures import ThreadPoolExecutor def add(a, b): sum = a + b slp = random.randint(0,60) time.sleep(slp) print('{} is running, {} + {} = {} ,thread sleep {} seconds'.format(threading.current_thread().name,a,b,sum,slp)) return sum list_a = [1, 2, 3, 4, 5, 6 ,7] list_b = [6, 7, 8, 9, 10 ,11 ,12] with ThreadPoolExecutor(2) as pool: # map中l(wèi)ist_a與list_b按照下標(biāo)一一對應(yīng) for result in pool.map(add, list_a, list_b): print('result = {}'.format(result))
運行截圖:

本文來自博客園,作者:業(yè)余磚家,轉(zhuǎn)載請注明原文鏈接:http://www.rzrgm.cn/yeyuzhuanjia/p/18286041

浙公網(wǎng)安備 33010602011771號