使用ThreadPoolExecutor可以簡潔地完成簡單多線程任務,獲取線程返回值的順序有兩種,一種是按線程創建順序返回,第二種是按線程完成順序返回(雖然返回順序不同,執行時間應該是一樣的)。
按線程創建順序返回
import time
import concurrent.futures
times = [3, 1, 2]
def sleeper(secs):
time.sleep(secs)
print('I slept for {} seconds'.format(secs))
return secs
# returns in the order given
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
print(list(executor.map(sleeper, times)))
# I slept for 1 seconds
# I slept for 2 seconds
# I slept for 3 seconds
# [3, 1, 2]
可以看到如果利用executor.map,其返回結果像是內置的map函數一般,對times執行完后返回times的返回值列表,其位置是一一對應的。
按線程完成順序返回
import time
import concurrent.futures
times = [3, 1, 2]
def sleeper(secs):
time.sleep(secs)
print('I slept for {} seconds'.format(secs))
return secs
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
futs = [executor.submit(sleeper, secs) for secs in times]
print([fut.result() for fut in concurrent.futures.as_completed(futs)])
# I slept for 1 seconds
# I slept for 2 seconds
# I slept for 3 seconds
# [1, 2, 3]
簡單說明一下as_completed函數,在線程使用中,傳入一個futures類列表,會返回一個futures類的迭代器,通過迭代該迭代器可以獲得已完成的futures(流暢的python中說的是該函數返回一個迭代器,在future運行結束后產出future)。
剛學的時候還想過這東西有個P用,給你一個futures類列表給我返回一個futures類的迭代器。但其實還挺有用的,可用作調整結果返回順序為線程完成順序。
有點杠的案例
在多線程中,如果對一個未完成的futures求result是會阻塞的當前線程的,試想如果你要對線程結果作處理,然而需要處理時間較多的線程在前面,直接迭代futures取結果會阻塞主線程,浪費主線程對線程結果處理的時間(當然你也完全可以直接把處理結果的過程放進每個線程中直接處理再返回)。例如以下情況
times = [4, 1, 2]
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
start_t = time.time()
futs = [executor.submit(sleeper, secs) for secs in times]
for fut in futs:
#模擬對線程結果作處理
time.sleep(1)
print(fut.result())
print(time.time()-start_t)
輸出結果如下
I slept for 1 seconds
I slept for 2 seconds
I slept for 4 seconds
4
1
2
6.034992456436157
可見主線程浪費了一秒,而用as_completed則是如下情況
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
start_t = time.time()
futs = [executor.submit(sleeper, secs) for secs in times]
for fut in concurrent.futures.as_completed(futs):
#模擬對fut。result()操作所需時間
time.sleep(1)
print(fut.result())
print(time.time()-start_t)
結果如下
I slept for 1 seconds
I slept for 2 seconds
1
2
I slept for 4 seconds
4
5.031828165054321
使用as_completed會按線程完成的順序返回,處理結果效率更高。
future類對於線程和協程來說都差不多,所以上述例子在協程中也很可能適用。
如有紕漏,歡迎斧正