之前已经介绍过了python的multiprocessing包相关的使用方法,实际上在python3.2版以后新加入了concurrent.futures模块。这个模块拥有线程池及进程池执行对象即Executor()。对比起多线程和多进程的其它模块,此模块包含了两种多进程多线程两种方式,且使用更加简单。使用时根据自身需要选择使用哪一个包。
concurrent.futures模块中含有ThreadPoolExecutor与ProcessPoolExecutor两个池执行器,操作方式类似,本文主要以ThreadPoolExecutor为例进行介绍
future对象
Future对象能够储存异步执行的任务,即其能代表一个正在执行的任务,这也说明了future对象的产生和运行是非阻塞的。future对象所接受的任务都是由ThreadPoolExecutor或者ProcessPoolExecutor产生的,future对象储存这些任务。通过调用future对象能够了解到这些任务的完成情况。因此future对象具有一系列的方法如下表:
方法 | 作用 |
---|---|
cancel() | 尝试取消调用,如果该调用正在执行中,无法取消,本方法返回 False,其他情况下调用会被取消,并返回 True |
cancelled() | 判断是否被取消 |
running() | 判断是否正在运行 |
result() | 获取结果 |
add_done_callback() | 添加回调函数 |
done() | 判断是否结束 |
exception | 获取异常 |
submit()
一个submit运行一次需要传入一个函数和执行一次函数需要的参数。因此调用一次submit只能执行单个任务,要使用submit执行多个任务就需要将不同的参数分配给submit,从而执行多线程任务。
例:
from concurrent.futures import ThreadPoolExecutor # 导入线程池执行器
import time # 导入time模块,计算花费时间
def test(g):
time.sleep(2) # 函数中休眠2s
return g # 设置返回值
if __name__ == '__main__':
record = [] # 创建列表放置后续产生的future对象
t = time.time() # 记录开始运行的时间
with ThreadPoolExecutor(max_workers=10) as executor:
# 线程池的使用方式,由于是with方式,因此不需要自己关闭。同时线程池会自动join()
# 当然也可以使用executor = ThreadPoolExecutor(max_workers=4),但需要在使用结束后使用
# shutdown方法,才能阻塞主线程。 代码也贴上啦,在下面。
for l in list(range(5)): # 创建一个参数列表用于后续传入函数
future = executor.submit(test,l)
# 传入要执行的函数及参数,返回的是一个future对象,注意这里不要调用,此处调用会使线程阻塞。
record.append(future) # 将对象添加到record列表中
for future in record:
print(future.result()) # 打印每个返回值
print('程序结束') # 测试主线程是否阻塞
print(time.time()-t) # 打印程序花费时间
结果如下:
可以看到运行结果达到了预期,即运行使用了多线程,本该运行10s的任务在2s内完成了。
另一种方法
from concurrent.futures import ThreadPoolExecutor
import time
def test(g):
time.sleep(2)
return g
if __name__ == '__main__':
record = []
t = time.time()
pool = ThreadPoolExecutor(max_workers=10)
for l in list(range(5)):
res = pool.submit(test,l)
record.append(res)
for p in record:
print(p.result())
pool.shutdown() # 关闭线程池,同时起到join的效果
print('程序结束')
print(time.time()-t)
map()
map同submit一样都是执行任务的,但是map可以同时添加多个任务。将某个可迭代的参数库与某函数直接map起来,较submit更为方便。
例:
from concurrent.futures import ThreadPoolExecutor
import time
def test(g):
time.sleep(2)
return g
if __name__ == '__main__':
t = time.time()
with ThreadPoolExecutor(max_workers=10) as pool:
results = pool.map(test,range(5)) # 产生一个生成器generator,可以从中获取结果
for result in results:
print(result)
print('程序结束')
print(time.time()-t)
运行结果如下:
wait()与as_completed()
- wait方法可以让主线程阻塞,直到满足设定的要求。有三种条件ALL_COMPLETED, FIRST_COMPLETED,FIRST_EXCEPTION
- as_completed同样也与主进程阻塞有关(即需要所有future对象都运行结束才结束阻塞)。
看例子理解一下吧,例:
from concurrent.futures import ThreadPoolExecutor,wait,as_completed,FIRST_COMPLETED
import time
def test(g):
time.sleep(2)
return g
if __name__ == '__main__':
record = []
t = time.time()
with ThreadPoolExecutor(max_workers=10) as executor:
for l in list(range(5)):
future = executor.submit(test,l)
record.append(future)
wait(record, return_when=FIRST_COMPLETED) # 第一次完成后即不再阻塞
print('第一次运行结束') # 测试第一次完成后是否不再阻塞
for future in as_completed(record): # 其实就相当于之前的shutdown操作
print(future.result()) # 并不按顺序提取,谁先完成就先打印,此处看不出来
print('程序结束')
print(time.time()-t)
结果如下:
叮!
此模块中多进程和多线程的使用方式应该差不多,这里就不再赘述咯!
多进程相关的内容看了很多,也写了好几篇,因为感觉挺重要的。虽说花了一些时间,但之后用上的时候应该会更加熟练一点吧!本文介绍的内容应该也足以应付平常用到的工作了吧。
参考:concurrent.futures进行并发编程
参考:Python 中 concurrent.futures 模块使用说明