使用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类对于线程和协程来说都差不多,所以上述例子在协程中也很可能适用。
如有纰漏,欢迎斧正