淘宝客做网站需要那些条件,信息技术网站建设市场分析,wordpress资源网源码,餐饮网站设计一文速通 Python 并行计算#xff1a;14 Python 异步编程-协程的管理和调度 摘要#xff1a;
Python 异步编程基于 async/await 构建协程#xff0c;运行在事件循环中。协程生成 Task#xff0c;遇到 await 时挂起#xff0c;I/O 完成触发回调恢复运行#xff0c;通过…一文速通 Python 并行计算14 Python 异步编程-协程的管理和调度 摘要
Python 异步编程基于 async/await 构建协程运行在事件循环中。协程生成 Task遇到 await 时挂起I/O 完成触发回调恢复运行通过事件循环非阻塞调度并发任务实现单线程高并发。 关于我们更多介绍可以查看云文档Freak 嵌入式工作室云文档或者访问我们的 wiki****https://github.com/leezisheng/Doc/wik 原文链接
FreakStudio的博客
往期推荐
学嵌入式的你还不会面向对象
全网最适合入门的面向对象编程教程00 面向对象设计方法导论
全网最适合入门的面向对象编程教程01 面向对象编程的基本概念
全网最适合入门的面向对象编程教程02 类和对象的 Python 实现-使用 Python 创建类
全网最适合入门的面向对象编程教程03 类和对象的 Python 实现-为自定义类添加属性
全网最适合入门的面向对象编程教程04 类和对象的Python实现-为自定义类添加方法
全网最适合入门的面向对象编程教程05 类和对象的Python实现-PyCharm代码标签
全网最适合入门的面向对象编程教程06 类和对象的Python实现-自定义类的数据封装
全网最适合入门的面向对象编程教程07 类和对象的Python实现-类型注解
全网最适合入门的面向对象编程教程08 类和对象的Python实现-property装饰器
全网最适合入门的面向对象编程教程09 类和对象的Python实现-类之间的关系
全网最适合入门的面向对象编程教程10 类和对象的Python实现-类的继承和里氏替换原则
全网最适合入门的面向对象编程教程11 类和对象的Python实现-子类调用父类方法
全网最适合入门的面向对象编程教程12 类和对象的Python实现-Python使用logging模块输出程序运行日志
全网最适合入门的面向对象编程教程13 类和对象的Python实现-可视化阅读代码神器Sourcetrail的安装使用
全网最适合入门的面向对象编程教程全网最适合入门的面向对象编程教程14 类和对象的Python实现-类的静态方法和类方法
全网最适合入门的面向对象编程教程15 类和对象的 Python 实现-__slots__魔法方法
全网最适合入门的面向对象编程教程16 类和对象的Python实现-多态、方法重写与开闭原则
全网最适合入门的面向对象编程教程17 类和对象的Python实现-鸭子类型与“file-like object“
全网最适合入门的面向对象编程教程18 类和对象的Python实现-多重继承与PyQtGraph串口数据绘制曲线图
全网最适合入门的面向对象编程教程19 类和对象的 Python 实现-使用 PyCharm 自动生成文件注释和函数注释
全网最适合入门的面向对象编程教程20 类和对象的Python实现-组合关系的实现与CSV文件保存
全网最适合入门的面向对象编程教程21 类和对象的Python实现-多文件的组织模块module和包package
全网最适合入门的面向对象编程教程22 类和对象的Python实现-异常和语法错误
全网最适合入门的面向对象编程教程23 类和对象的Python实现-抛出异常
全网最适合入门的面向对象编程教程24 类和对象的Python实现-异常的捕获与处理
全网最适合入门的面向对象编程教程25 类和对象的Python实现-Python判断输入数据类型
全网最适合入门的面向对象编程教程26 类和对象的Python实现-上下文管理器和with语句
全网最适合入门的面向对象编程教程27 类和对象的Python实现-Python中异常层级与自定义异常类的实现
全网最适合入门的面向对象编程教程28 类和对象的Python实现-Python编程原则、哲学和规范大汇总
全网最适合入门的面向对象编程教程29 类和对象的Python实现-断言与防御性编程和help函数的使用
全网最适合入门的面向对象编程教程30 Python的内置数据类型-object根类
全网最适合入门的面向对象编程教程31 Python的内置数据类型-对象Object和类型Type
全网最适合入门的面向对象编程教程32 Python的内置数据类型-类Class和实例Instance
全网最适合入门的面向对象编程教程33 Python的内置数据类型-对象Object和类型Type的关系
全网最适合入门的面向对象编程教程34 Python的内置数据类型-Python常用复合数据类型元组和命名元组
全网最适合入门的面向对象编程教程35 Python的内置数据类型-文档字符串和__doc__属性
全网最适合入门的面向对象编程教程36 Python的内置数据类型-字典
全网最适合入门的面向对象编程教程37 Python常用复合数据类型-列表和列表推导式
全网最适合入门的面向对象编程教程38 Python常用复合数据类型-使用列表实现堆栈、队列和双端队列
全网最适合入门的面向对象编程教程39 Python常用复合数据类型-集合
全网最适合入门的面向对象编程教程40 Python常用复合数据类型-枚举和enum模块的使用
全网最适合入门的面向对象编程教程41 Python常用复合数据类型-队列FIFO、LIFO、优先级队列、双端队列和环形队列
全网最适合入门的面向对象编程教程42 Python常用复合数据类型-collections容器数据类型
全网最适合入门的面向对象编程教程43 Python常用复合数据类型-扩展内置数据类型
全网最适合入门的面向对象编程教程44 Python内置函数与魔法方法-重写内置类型的魔法方法
全网最适合入门的面向对象编程教程45 Python实现常见数据结构-链表、树、哈希表、图和堆
全网最适合入门的面向对象编程教程46 Python函数方法与接口-函数与事件驱动框架
全网最适合入门的面向对象编程教程47 Python函数方法与接口-回调函数Callback
全网最适合入门的面向对象编程教程48 Python函数方法与接口-位置参数、默认参数、可变参数和关键字参数
全网最适合入门的面向对象编程教程49 Python函数方法与接口-函数与方法的区别和lamda匿名函数
全网最适合入门的面向对象编程教程50 Python函数方法与接口-接口和抽象基类
全网最适合入门的面向对象编程教程51 Python函数方法与接口-使用Zope实现接口
全网最适合入门的面向对象编程教程52 Python函数方法与接口-Protocol协议与接口
全网最适合入门的面向对象编程教程53 Python字符串与序列化-字符串与字符编码
全网最适合入门的面向对象编程教程54 Python字符串与序列化-字符串格式化与format方法
全网最适合入门的面向对象编程教程55 Python字符串与序列化-字节序列类型和可变字节字符串
全网最适合入门的面向对象编程教程56 Python字符串与序列化-正则表达式和re模块应用
全网最适合入门的面向对象编程教程57 Python字符串与序列化-序列化与反序列化
全网最适合入门的面向对象编程教程58 Python字符串与序列化-序列化Web对象的定义与实现
全网最适合入门的面向对象编程教程59 Python并行与并发-并行与并发和线程与进程
一文速通Python并行计算00 并行计算的基本概念
一文速通Python并行计算01 Python多线程编程-基本概念、切换流程、GIL锁机制和生产者与消费者模型
一文速通Python并行计算02 Python多线程编程-threading模块、线程的创建和查询与守护线程
一文速通Python并行计算03 Python多线程编程-多线程同步上—基于互斥锁、递归锁和信号量
一文速通Python并行计算04 Python多线程编程-多线程同步下—基于条件变量、事件和屏障
一文速通Python并行计算05 Python多线程编程-线程的定时运行
一文速通Python并行计算06 Python多线程编程-基于队列进行通信
一文速通Python并行计算07 Python多线程编程-线程池的使用和多线程的性能评估
一文速通Python并行计算08 Python多进程编程-进程的创建命名、获取ID、守护进程的创建和终止进程
一文速通Python并行计算09 Python多进程编程-进程之间的数据同步-基于互斥锁、递归锁、信号量、条件变量、事件和屏障
一文速通Python并行计算10 Python多进程编程-进程之间的数据共享-基于共享内存和数据管理器
一文速通Python并行计算11 Python多进程编程-进程之间的数据安全传输-基于队列和管道
一文速通Python并行计算12 Python多进程编程-进程池Pool
一文速通Python并行计算13 Python异步编程-基本概念与事件循环和回调机制
更多精彩内容可看
C语言一点五编程实战纯 C 的模块化×继承×多态框架
给你的 Python 加加速一文速通 Python 并行计算
一文搞懂 CM3 单片机调试原理
肝了半个月嵌入式技术栈大汇总出炉
电子计算机类比赛的“武林秘籍”
一个MicroPython的开源项目集锦awesome-micropython包含各个方面的Micropython工具库
Avnet ZUBoard 1CG开发板—深度学习新选择
工程师不要迷信开源代码还要注重基本功
什么配色个性化的电机驱动模块
什么XIAO主控新出三款扩展板!
手把手教你实现Arduino发布第三方库
万字长文手把手教你实现MicroPython/Python发布第三方库
一文速通电子设计大赛电子人必看的获奖秘籍
一文速通光电设计大赛电子人必看
工科比赛“无脑”操作指南知识学习硬件选购→代码调试→报告撰写的保姆级路线图
单场会议拍摄收费6000拍摄技巧和步骤都在这里
0基础如何冲击大唐杯国奖学姐的的备赛心得都在这里
爆肝整理长文】大学生竞赛速通指南选题 × 组队 × 路演 48 小时备赛搞定
当代大学生竞赛乱象从“内卷”到“祖传项目”的生存指南
女大学生摆摊亏损5000元踩点实录成都哪里人最多、最容易赚到钱我告诉你
用拍立得在成都网红打卡点赚钱一份超实用地摊级旅游副业教程
成都印象一个电子女孩亲手做了点浪漫
普通继电器 vs 磁保持继电器 vs MOS管工作原理与电路设计全解析
告别TP4056国产SY3501D单芯片搞定充放电升压仅需7个元件附开源PCB文件
POB面向老板编程—现实驱动的新型编程范式
文档获取
可访问如下链接进行对文档下载
https://github.com/leezisheng/Doc
该文档是一份关于 并行计算 和 Python 并发编程 的学习指南内容涵盖了并行计算的基本概念、Python 多线程编程、多进程编程以及协程编程的核心知识点 正文
在上文提到的例子中我们看到当一个程序变得很大而且复杂时将其划分为子程序每一部分实现特定的任务是个不错的方案。子程序不能单独执行只能在主程序的请求下执行主程序负责协调使用各个子程序。协程就是子程序的泛化。和子程序一样的事协程只负责计算任务的一步和子程序不一样的是协程没有主程序来进行调度。这是因为协程通过管道连接在一起没有监视函数负责顺序调用它们。
对于子程序来说调用是通过栈实现的子程序调用总是一个入口一次返回调用顺序是明确的。比如 A 调用 BB 在执行过程中又调用了 CC 执行完毕返回B 执行完毕返回最后是 A 执行完毕。协程看上去也是子程序但执行过程中在子程序内部可中断然后转而执行别的子程序在适当的时候再返回来接着执行。从心理学的角度看协程类似于人类的“任务切换”能力我们可以暂时将注意力从一个任务转移到另一个然后再回来继续未完成的任务。
或者说**在协程中执行点可以被挂起可以被从之前挂起的点恢复执行。**通过协程池就可以插入到计算中运行第一个任务直到它返回(yield)执行权然后运行下一个这样顺着执行下去。这种插入的控制组件就是前文介绍的事件循环。它持续追踪所有的协程并执行它们。
协程的另外一些重要特性如下
协程可以有多个入口点并可以 yield 多次协程可以将执行权交给其他协程
yield 表示协程在此暂停并且将执行权交给其他协程。因为协程可以将值与控制权一起传递给另一个协程所以“yield 一个值”就表示将值传给下一个执行的协程。
协程与传统的线程或进程相比有几个关键区别
**1轻量级**协程通常是用户态的子程序切换函数不是线程切换由程序自身控制切换开销比系统线程小得多。
**2非抢占式**协程的切换是协作式的即协程需要显式地 yield 来让出控制权。
**3更好的控制**协程提供了更细粒度的控制如何以及何时切换是由程序员或协程库决定的。
协程可以处理 IO 密集型程序的效率问题但是处理 CPU 密集型不是它的长处如要充分发挥 CPU 利用率可以结合多进程 协程。
1.使用 Asyncio 管理协程
Python3.x 提供了如下方式实现协程
asyncio yield from (python3.4)
asyncio 是 Python3.4 版本引入的标准库直接内置了对异步 IO 的支持。asyncio 的异步操作需要在 coroutine 中通过 yield from 完成。
import asyncioasyncio.coroutine
def test(i):print(test_1, i)r yield from asyncio.sleep(1)print(test_2, i)if __name__ __main__:loop asyncio.get_event_loop()tasks [test(i) for i in range(3)]loop.run_until_complete(asyncio.wait(tasks))loop.close()asyncio.coroutine 使用装饰器定义了一个协程。所谓装饰器是给现有的模块增添新的小功能的函数它可以对原函数进行功能扩展而且还不需要修改原函数的内容也不需要修改原函数的调用。
使用 asyncio.coroutine 定义协程的通用方法如下
import asyncioasyncio.coroutine
def coroutine_function(function_arguments):_# DO_SOMETHING_以上代码将 test(i) 定义为一个协程然后就把这个协程放到事件循环中执行。test(i) 首先执行打印操作这里用 asyncio.sleep(1) 模拟一个耗时 1 秒的 IO 操作asyncio.sleep() 本身也是一个协程这里使用 yield from 语法调用 asyncio.sleep()但注意线程不会等待 asyncio.sleep()而是直接中断并执行下一个消息循环。当 asyncio.sleep() 返回时线程就可以从 yield from 拿到返回值此处是 None然后接着执行下一行语句。由此实现异步执行。
asyncio async/await (python3.5)
为了简化并更好地标识异步 IO从 Python3.5 开始引入了新的语法 async 和 await可以让 coroutine 的代码更简洁易读。请注意async 和 await 是 coroutine 的新语法使用新语法只需要做两步简单的替换
把 asyncio.coroutine 替换为 async把 yield from 替换为 await即让出当前的执行权等待的对象有结果返回时再重新获得可以被继续执行的权利只有可等待对象才能被 await。
注意包含 asyncio.coroutine 装饰器的将从 Python3.11 中删除因此 asyncio 模块没有 asyncio.coroutine 装饰符。
import asyncio
async def test(i):print(test_1, i)await asyncio.sleep(1)print(test_2, i)if __name__ __main__:loop asyncio.new_event_loop()asyncio.set_event_loop(loop)_# 在python3.8后直接把协程对象传给asyncio.wait()是不行的__# 必须封装成tasks对象传入_tasks [loop.create_task(test(1)),loop.create_task(test(2)),loop.create_task(test(3))]loop.run_until_complete(asyncio.wait(tasks))loop.close()除了 await 方法asyncio 提供了**asyncio.run()**来执行协程
run() 函数接收一个协程对象在执行时总会**创建一个新的事件循环并在结束后关闭循环。****理想情况下****run() ****函数应当被作为程序的总入口并且只会被调用一次。**如果同一线程中还有其它事件循环在运行则此方法不能被调用。 2.使用 Asyncio 控制任务
Asyncio 是用来处理事件循环中的异步进程和并发任务执行的。**它还提供了 asyncio.Task() 类可以在任务中使用协程。**它的作用是在同一事件循环中,**运行某一个任务的同时可以并发地运行多个任务。当协程被包在任务中它会自动将任务和事件循环连接起来当事件循环启动的时候任务自动运行。**这样就提供了一个可以自动驱动协程的机制。 2.1 asyncio.create_task() 下面的例子中我们使用 asyncio.create_task() 创建 Taskcreate_task() 会把一个协程打包成一个任务Task并立即排入日程准备执行函数返回值是打包完成的 Task 对象。
import asyncio
import time
async def foo(n):await asyncio.sleep(n)async def main():task1 asyncio.create_task(foo(1))task2 asyncio.create_task(foo(2))t1 time.time()print(hello)await task1await task2print(coroutine)t2 time.time()print(cost:, t2 - t1)asyncio.run(main())如下为运行结果 当使用 create_task() 时创建的任务立即被加入到事件循环中并不会阻塞当前的程序所以上述程序在打印出 hello 后只需等待 2 秒就打印出 coroutine。
2.2 asyncio.gather()
asyncio.gather() 函数允许调用者将多个可等待对象组合在一起。分组后可等待对象可以并发执行、等待和取消。它是一个有用的实用函数可用于分组和执行多个协程或多个任务。 从功能上看asyncio.wait 和 asyncio.gather 实现的效果是相同的都是把所有 Task 任务结果收集起来。但不同的是asyncio.wait 会返回两个值done 和 pendingdone 为已完成的协程 Taskpending 为超时未完成的协程 Task需通过 future.result 调用 Task 的 result而 asyncio.gather返回的是所有已完成Task的result**不需要再进行调用或其他操作就可以得到全部结果。**
import asyncioasync def foo():return foo
async def bar():raise RuntimeError(fake runtime error)
async def main():task1 asyncio.create_task(foo())task2 asyncio.create_task(bar())_# return_exceptionsTrue__# 如果return_exceptions为True__# 异常会和成功的结果一样处理并聚合至结果列表_results await asyncio.gather(task1, task2, return_exceptionsTrue)print(results)_# 返回结果的顺序和传参顺序一致__# isinstance() 函数来判断一个对象是否是一个已知的类型__# isinstance(object, classinfo)_assert isinstance(results[1], RuntimeError)try:_# 如果return_exceptions为False(默认)__# 所引发的首个异常会立即传播给等待gather()的任务_results await asyncio.gather(task1, task2, return_exceptionsFalse)_# 此处打印并不会被执行, results 也未被赋值_print(results)except RuntimeError as runtime_err:_# 捕获异常并打印: fake runtime error_print(runtime_err)asyncio.run(main())执行结果如下 2.3 asyncio.wait()
asyncio.wait() 函数可用于等待一组异步任务完成回想一下asyncio 任务是包装协程的 asyncio.Task 类的一个实例。它允许独立调度和执行协程Task 实例提供任务句柄以查询状态和获取结果。**wait()**函数允许我们等待一组任务完成。等待调用可以配置为等待不同的条件例如所有任务完成、第一个任务完成以及第一个任务因错误而失败。 asyncio.wait 最常见的写法是await asyncio.wait(task_list)表示运行直到所有给定的协程都完成。常见写法为
...
_# create many tasks_
tasks [asyncio.create_task(task_coro(i)) for i in range(10)]示例代码如下
import asyncioasync def foo():await asyncio.sleep(3)return foo
async def bar():await asyncio.sleep(1)return bar
async def main():task1 asyncio.create_task(foo())task2 asyncio.create_task(bar())_# 有一个任务执行完成即返回, 总共耗时 1 秒_done, pending await asyncio.gather(task1, task2, return_exceptionsTrue)_# done 集合里包含打包成 Task 的 bar()_print(fdone: {done})_# pendding 集合里包含打包成 Task 的 foo()_print(fpending: {pending})_# 所有任务执行完成后返回, 总共耗时 3 秒_done, pending await asyncio.gather(task1, task2, return_exceptionsTrue)_# done 集合里包含被带打包成 Task 的 foo() 和 bar()_print(fdone: {done})_# pending 集合为空_print(fpending: {pending})_# 所有任务执行完成, 但运行时间不能超 2 秒后返回, 总共耗时 2 秒_done, pending await asyncio.gather(task1, task2, return_exceptionsTrue)_# done 集合里包含打包成 Task 的 bar()_print(fdone: {done})_# pendding 集合里包含打包成 Task 的 foo()_print(fpending: {pending})
asyncio.run(main())2.4 asyncio.as_completed()
有时我们必须在完成一个后台任务后立即开始下面的动作。比如我们爬取一些数据马上调用机器学习模型进行计算gather 方法不能满足我们的需求但是我们可以使用 as_completed 方法。 as_completed 不是并发方法接受 aws 集合返回一个带有 yield 语句的迭代器。所以我们可以直接遍历每个完成的后台任务我们可以对每个任务单独处理异常而不影响其他任务的执行
示例代码如下
import asyncioasync def foo():await asyncio.sleep(2)return fooasync def bar():await asyncio.sleep(1)return barasync def main():for fut in asyncio.as_completed({foo(), bar()}):earliest_result await fut_# 会依次打印 bar 和 foo, 因为 bar() 会更早执行完毕_print(earliest_result)asyncio.run(main())以上介绍多任务并发时引入了超时的概念超时也可以被应用在单独的一个任务中使用 asyncio.wait_for(aw, timeout) 函数该函数接受一个任务 aw 和超时时间 timeout如果在限制时间内完成则会正常返回否则会被取消并抛出 asyncio.TimeoutError 异常。
为了防止任务被取消可以使用 asyncio.shield(aw) 进行保护。shield() 会屏蔽外部取消操作如果外部任务被取消其内部正在执行的任务不会被取消在内部看来取消操作并没有发生由于内部仍正常执行执行完毕后会触发异常如果确保程序能忽略异常继续执行需要在外部使用 try-except 捕获异常。如果在任务内部取消则会被成功取消。
Asyncio 模块为我们提供了 asyncio.Task(coroutine) 方法来处理计算任务它可以调度协程的执行。任务对协程对象在事件循环的执行负责。如果被包裹的协程要从 future就是协程的实例化对象调度那么任务会被挂起等待 future 的计算结果。
当 future 计算完成被包裹的协程将会拿到 future 返回的结果或异常继续执行。另外需要注意的是事件循环一次只能运行一个任务除非还有其它事件循环在不同的线程并行运行此任务才有可能和其他任务并行。当一个任务在等待 future 执行的期间事件循环会运行一个新的任务。
在下面的代码中我们展示了三个可以被 Asyncio.Task() 并发执行的数学函数在这个例子中我们定义了三个协程 factorial, fibonacci 和 binomialCoeff 为了能并行执行这三个任务我们将其放到一个 task 的 list 中得到事件循环然后运行任务最后关闭事件循环。
import asyncioasync def factorial(number):f 1for i in range(2, number 1):print(Asyncio.Task: Compute factorial(%s) % (i))await asyncio.sleep(1)f * iprint(Asyncio.Task - factorial(%s) %s % (number, f))async def fibonacci(number):a, b 0, 1for i in range(number):print(Asyncio.Task: Compute fibonacci (%s) % (i))await asyncio.sleep(1)a, b b, a bprint(Asyncio.Task - fibonacci(%s) %s % (number, a))async def binomialCoeff(n, k):result 1for i in range(1, k1):result result * (n-i1) / iprint(Asyncio.Task: Compute binomialCoeff (%s) % (i))await asyncio.sleep(1)print(Asyncio.Task - binomialCoeff(%s , %s) %s % (n, k, result))if __name__ __main__:tasks [asyncio.Task(factorial(10)),asyncio.Task(fibonacci(10)),asyncio.Task(binomialCoeff(20, 10))]loop asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))loop.close()运行结果如下 3.使用 Asyncio 和 Futures
future 是一个 Python 对象它包含一个你希望在未来某个时间点获得、但目前还不存在的值。通常当创建 future 时它没有任何值因为它还不存在。在这种状态下它被认为是不完整的、未解决的或根本没有完成的。然后一旦你得到一个结果就可以设置 future 的值这将完成 future。那时我们可以认为它已经完成并可从 future 中提取结果。 要操作 Asyncio 中的 Future 必须进行以下声明
import asyncio
future asyncio.Future()基本的方法有
方法作用cancel()取消 future 的执行调度回调函数result()返回 future 代表的结果exception()返回 future 中的 Exceptionadd_done_callback(fn)添加一个回调函数当 future 执行的时候会调用这个回调函数remove_done_callback(fn)从“call whten done”列表中移除所有 callback 的实例set_result(result)将 future 标为执行完成并且设置 result 的值set_exception(exception)将 future 标为执行完成并设置 Exception
import asyncio_# asyncio 里面有一个类 Future实例化之后即可得到 future 对象_
_# 然后 asyncio 里面还有一个类 Task实例化之后即可得到 task 对象也就是任务_
_# 这个 Task 是 Future 的子类所以我们用的基本都是 task 对象而不是 future 对象_
_# 但 Future 这个类和 asyncio 的实现有着密不可分的关系所以我们必须单独拿出来说_future asyncio.Future()
print(future) _# Future pending_
print(future.__class__) _# class _asyncio.Future_
print(ffuture 是否完成: {future.done()}) _# future 是否完成: False__# 设置一个值通过 set_result_
future.set_result(古明地觉)
print(ffuture 是否完成: {future.done()}) _# future 是否完成: True_
print(future) _# Future finished result古明地觉_
print(ffuture 的返回值: {future.result()}) _# future 的返回值: 古明地觉_可通过调用其类型对象 Future 来创建 future此时 future 上将没有结果集因此调用其 done 方法将返回 False。此后用 set_result 方法设置 future 的值这将把 future 标记为已完成。或者如果想在 future 中设置一个异常可调用 set_exception。必须在调用set_result设置结果之后才能调用result获取结果并且set_result只能调用一次但result可以调用多次
在下面的示例代码中我们定义了一个函数 make_request该函数里面创建了一个 future 和一个任务该任务将在 1 秒后异步设置 future 的结果。然后在主函数中调用 make_request当调用它时将立即得到一个没有结果的 future。然后 await future 会让主协程陷入等待并将执行权交出去。一旦当 future 有值了那么再恢复 main() 协程拿到返回值进行处理。
import asyncioasync def set_future_value(future):await asyncio.sleep(1)future.set_result(Hello World)
def make_request():future asyncio.Future()_# 创建一个任务来异步设置 future 的值_asyncio.create_task(set_future_value(future))return future
async def main():_# 注意这里的 make_request它是一个普通的函数如果在外部直接调用肯定是会报错的__# 因为没有事件循环在执行 set_future_value 时会报错__# 但如果在协程里面调用是没问题的因为协程运行时事件循环已经启动了__# 此时在 make_request 里面会启动一个任务_future make_request()print(ffuture 是否完成: {future.done()})_# 阻塞等待直到 future 有值什么时候有值呢__# 显然是当协程 set_future_value 里面执行完 future.set_result 的时候_value await future _# 暂停 main()直到 future 的值被设置完成_print(ffuture 是否完成: {future.done()})print(value)
asyncio.run(main())