内容简介:原创不易,转载请联系作者本篇为
原创不易,转载请联系作者
深入理解协程
分为三部分进行讲解:
- 协程的引入
- yield from实现异步协程
- async/await实现异步协程
本篇为 深入理解协程
系列文章的 最后一篇
。
从本篇你将了解到:
-
async/await
的使用。 -
如何从
yield from
风格的协程修改为async/await
风格。
篇幅较长,请耐心阅读。
async/await的引入
上篇 【yield from实现异步协程】
我们引入了 asynico
模块,结合 yield from
实现异步协程。但语法不够简洁,其中涉及的 生成器
, 装饰器
也让人头疼不已。
为了语法更加简洁。于是,在 Python 3.5(PEP 492)中新增了 async/await
语法来实现异步协程。
async/await的使用
先介绍几个概念:
- async/await :python3.5之后用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。
- event_loop :事件循环,程序开启一个无限的循环,程序员会把一些函数注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。
- coroutine :协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
- task :任务,是对协程进一步封装,其中包含任务的各种状态。
- future : 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别
1.创建协程
在def前加上async的声明,就完成了一个协程函数的定义。 协程函数不能直接调用运行,需要将协程注册到事件循环,并启动事件循环才能使用 。
import asyncio async def fun(a): # 定义协程函数 print(a) # 调用协程函数,生成一个协程对象,此时协程函数并未执行 coroutine = fun('hello world') # 创建事件循环 loop = asyncio.get_event_loop() # 将协程函数添加到事件循环,并启动 loop.run_until_complete(coroutine) # 输出 hello word
2. 任务对象task
协程对象不能直接运行,在注册事件循环的时候,其实是run_until_complete方法将协程包装成为了一个任务(task)对象。我们也可以显式实现它。
实现方式1:
使用create_task()创建task。
import asyncio async def fun(a): print(a) return a coroutine = fun('hello world') loop = asyncio.get_event_loop() # 使用create_task()创建task,并将coroutine对象转化成task对象 task = loop.create_task(coroutine) print(f'task: {task}') loop.run_until_complete(task) print(f'task: {task}') print(f'result: {result}')
输出结果:
task: <Task pending coro=<fun() running at D:/test.py:3>> hello world task: <Task finished coro=<fun() done, defined at D:/test.py:3> result='hello world'>
从输出结果能看到,创建task对象后,未将task添加到事件循环之前,状态是 pending
;task对象执行完毕后,状态是 finished
,并将参数 a
的值返回。
实现方式2:
使用asyncio 的 ensure_future() 方法,创建task。
import asyncio async def fun(a): print(a) return a coroutine = fun('hello world') # 使用asyncio 的 ensure_future() 方法,创建task,并将coroutine对象转化成task对象 task = asyncio.ensure_future(coroutine) loop = asyncio.get_event_loop() print(f'task: {task}') loop.run_until_complete(task) print(f'task: {task}')
输出结果:
task: <Task pending coro=<fun() running at D:/test.py:3>> hello world task: <Task finished coro=<fun() done, defined at D:/test.py:3> result='hello world'>
通过 ensure_future()
可以在 loop
未定义前创建task。实现效果与上面相同。
3.绑定回调函数
如果需要在task执行完毕后对结果进行处理,可以通过给task绑定回调函数完成,回调的最后一个参数是future对象(如task对象)。
import asyncio async def fun(a): print(a) return a def callback(task): # 回调函数,打印task的返回值 print(f'result: {task.result()}') coroutine = fun('hello world') loop = asyncio.get_event_loop() task = loop.create_task(coroutine) task.add_done_callback(callback) #绑定回调函数 print(f'task: {task}') loop.run_until_complete(task) print(f'task: {task}')
输出结果:
task: <Task pending coro=<fun() running at D:/test.py:3> cb=[callback() at D:/Study/Python/python_text/非项目/协程.py:7]> hello world result: hello world # 完成了返回值的打印 task: <Task finished coro=<fun() done, defined at D:/test.py:3> result='hello world'>
4.多任务协程
如果我们需要执行多个任务时,我们可以定义一个任务列表,并将需要完成的协程任务都加进去。将原本的 loop.run_until_complete(tasks)
改为 loop.run_until_complete(asyncio.wait(tasks))
。
如果执行的是多个 耗时
的任务,如网络请求、文件读取等。此时就 await
就派上用场了, await
可以针对耗时的操作进行挂起,就像生成器里的yield一样,函数让出控制权。 协程遇到await,事件循环将会挂起该协程,执行别的协程,直到其他的协程也挂起或者执行完毕,再进行下一个协程的执行
。
举个例子:
import time import asyncio async def taskIO_1(): print('开始运行IO任务1...') await asyncio.sleep(2) print('IO任务1已完成,耗时2s') return taskIO_1.__name__ async def taskIO_2(): print('开始运行IO任务2...') await asyncio.sleep(3) print('IO任务2已完成,耗时3s') return taskIO_2.__name__ if __name__ == '__main__': start = time.time() loop = asyncio.get_event_loop() tasks = [taskIO_1(), taskIO_2()] loop.run_until_complete(asyncio.wait(tasks)) # 完成事件循环,直到最后一个任务结束 print('所有IO任务总耗时%.5f秒' % float(time.time()-start)) # 输出 开始运行IO任务2... 开始运行IO任务1... IO任务1已完成,耗时2s IO任务2已完成,耗时3s 所有IO任务总耗时3.00251秒
可以看出,原本需要5秒,现在执行只需要3秒。
yield from转async/await
上述代码有没有很眼熟。
其实,这段代码正是 【yield from实现异步协程】
末尾, yield from
结合 asynico
实现异步协程的代码。只是将yielf from风格变为async/await风格。
由于 async/await
与 yield from
风格的协程底层实现方式相同。因此,从 yield from
风格改为 async/await
风格非常容易。只需:
-
把
@asyncio.coroutine
替换为async
; -
把
yield from
替换为await
。
async/await
风格的代码隐藏了装饰器、 yield from
语法,方便了人们的理解,同时也让代码更加简洁。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 深入浅出 Rust 异步编程之 Tokio
- 原 荐 Java异步编程——深入源码分析FutureTask
- 深入剖析通信层和 RPC 调用的异步化(上)
- 深入剖析通信层和 RPC 调用的异步化(下)
- 获奖结果|分布式系统关注点——深入浅出「异步」
- SpringBoot | :异步开发之异步调用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
赛博空间的奥德赛
(荷兰)约斯·德·穆尔 (Jos de Mul) / 麦永雄 / 广西师范大学出版社 / 2007-2 / 38.00元
本书揭示了数码信息时代的电子传媒与赛博空间为人类历史的发展提供的新的可能性。本书第一部分“通向未来的高速公路”,涉及无线想象、政治技术和极权主义在赛博空间的消解等题旨;第二部分“赛博空间的想象” ,讨论空间文学探索简史、电影和文化的数码化;第三部分”可能的世界” ,关涉世界观的信息化、数码复制时代的世界、数码此在等层面;第四、五部分探讨主页时代的身份、虚拟人类学、虚拟多神论、赛博空间的进化、超人文......一起来看看 《赛博空间的奥德赛》 这本书的介绍吧!