「这是我参与2022首次更文挑战的第24天,活动详情查看:2022首次更文挑战」。
正式的Python专栏第73篇,同学站住,别错过这个从0开始的文章!
上篇,学委展示了线程池,我们看到ThreadPoolExecutor提供了submit方法,提交一个任务。
不过它有返回值,它的返回值是Future类的一个实例对象。
通过这个对象我们可以获取到提交任务的执行结果。
怎么获取呢?请继续往下看。
Future
concurrent.futures.Future这个类是用来获取线程任务返回结果的。
跟Thread类不一样,线程对象运行完毕,它不会给我们线程内部执行的方法的结果,但是Future可以。
Executor调用submit的结果 跟 线程运行的对比
学委准备了下面的代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/2/23 10:20 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : futuredemo.py
# @Project : DeepDivePython
import concurrent.futures
import threading
import time
from concurrent.futures import Future
from concurrent.futures.thread import ThreadPoolExecutor
from datetime import datetime
def job01(slogan):
name = threading.currentThread().name
print("%s start thread-name %s - slogan %s" % (datetime.now(), name, slogan))
time.sleep(3)
print("%s finish thread-name %s" % (datetime.now(), name))
return "job01 data: " + str(slogan)
#线程池也支持with风格的资源管理模式
with ThreadPoolExecutor() as executor:
future1: Future = executor.submit(job01, ("持续学习", "持续开发",))
print("future1:", future1)
result = future1.result()
print("result1:", result)
print("future1:", future1)
t = threading.Thread(target=job01, kwargs={"slogan": ("持续学习", "持续开发")})
result = t.start()
t.join()
print("thread run result:", result)
运行效果如下:
通过运行上面的代码,我们看到线程类start后并不返回任何结果。
尽管我们使用了join,等到线程运行结束result对象也依然是空。
但是future1这个对象却保证了程序必定能够获得线程内任务的执行结果。
另外,这里也补充一下Executor类资源池,也支持with风格,在with代码快结束后,池内资源自动回收。
future的方法展示
future类提供的方法还不少。
-
result : 获取线程调用方法的返回结果。上面展示了。
-
cancel:中途尝试取消executor提交的线程,可能取消不了。
-
done/cancelled/running: 等状态查看方法来探知future对象的不同状态。也就是相应的:任务完成/任务取消/任务运行中。
解释干巴巴的,我们直接看代码会更高效:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/2/23 10:20 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : futuredemo.py
# @Project : DeepDivePython
import concurrent.futures
import threading
import time
from concurrent.futures import Future
from concurrent.futures.thread import ThreadPoolExecutor
from datetime import datetime
def job01(args):
name = threading.currentThread().name
print("%s thread-name %s - slogan %s" % (datetime.now(), name, args))
time.sleep(3)
return "job01 data: " + str(args)
def job02(args):
name = threading.currentThread().name
print("%s thread-name %s - slogan %s" % (datetime.now(), name, args))
time.sleep(5)
return "job02 data: " + str(args)
with ThreadPoolExecutor() as executor:
future1: Future = executor.submit(job01, ("持续学习", "持续开发",))
future2: Future = executor.submit(job02, ("我是雷学委",))
cancel_status = future2.cancel() # 取消线程执行
print("future1:", future1)
print("展示future对象各个方法:future2:%s, status:%s, running:%s, cancelled:%s, done:%s" % (
future2, cancel_status, future2.running(), future2.cancelled(), future2.done()
))
#future2.set_running_or_notify_cancel()
#future2.set_exception(InterruptedError("Fail"))
result = future1.result()
print("result1:", result)
result = future2.result()
print("result2:", result)
print("future1:", future1)
print("future2:", future2)
上面学委提交了两次分别submit job01和job02,依次获取了future1,future2对象 。
代码运行结果如下:
然后尝试对future2代表的任务发送一个cancel信号,但是很遗憾,本次cancel没有成功。
future2背后的任务依然正常运行,后面还获取到了符合预期的返回值。
如果我们把这行代码取消注释,我们会看到future2的result返回(抛出)一个异常。
future2.set_exception(InterruptedError("Fail"))
另外还有提供钩子,可以给我们在返回结果前进行回调,具体如下:
#雷学委demo代码
future3: Future = executor.submit(job02, ("我是雷学委",))
future3.add_done_callback(lambda x:print("下次再见,拜拜!"))
print("future3:",future3.result())
总结
Future类提供了很多方法,除了让我们友好的通过它获取到一个异步任务的结果。(用同步的方式获得)。
如果我们调用result传入一个时间,那么也可以不必等待,程序会在timeout的时候推出,这时候我们需要进行异常处理,后面在其他并发类中继续分享。
另外,还可以让我们对它进行监控(各种状态函数)和钩子,实现对池内并发任务的灵活管理。
喜欢Python的朋友,请关注学委的 Python基础专栏 or Python入门到精通大专栏
持续学习持续开发,我是雷学委!
编程很有趣,关键是把技术搞透彻讲明白。
欢迎关注微信,点赞支持收藏!