实例分析asyncio实现python异步任务
1.分析对象asyncio
asyncio包是python之父Guido在python仓库之外开发的一个库,其作用是使用事件循环驱动的协程实现并发。asyncio类型的事件驱动框架的运作方式为:在单个线程中使用一个主循环驱动协程执行并发活动,使用协程进行并发编程时,协程会不断把控制权让步给主循环,激活并向前运行其他协程,从而执行各个并发活动。
2.了解yield from句法
yield from:asyncio包中使用了大量的yield from语句,可以说yield from句法的特性是asyncio包实现异步调度线程的最大功臣。把职责委托给子生成器的句法,使用它可以把复杂的生成器重构成小型的嵌套生成器,省去了之前把生产器的工作委托给子生成器所需的大量样本代码。在协程中,yield from的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。有了这个结构,协程可以通过以前不可能的方式委托职责。以下是在协程中使用yield from语句需要了解的新的术语:
- 委派生成器
- 包含yield from表达式的生成器函数,委派生成器在yield from表达式处暂停时,调用方可以直接把数据发给子生成器,子生成器再把产出的值发给调用法。子生成器返回之后,解释器会抛出StopIteration异常,并把返回值附加在异常对象上,此时委派生成器会恢复
- 子生成器:
- 从yield from表达式中部分获取的生成器。
- 调用方
- 委派生成器的客户端代码
如果你想要具体了解协程,可访问此链接:https://blog.csdn.net/jasonzhoujx/article/details/81506303
3.实例源码
import asyncio
import sys
import itertools
#使用@asyncio.coroutine装饰器不是强制要求,但强烈建议这么做,因为这样可以在一众普通函数中把协程凸显出#来,也有助于调试
@asyncio.coroutine
def slow_down():
# 假装等待I/O一段时间
yield from asyncio.sleep(3)
return 42
@asyncio.coroutine
def spin(msg):
write, flush = sys.stdout.write, sys.stdout.flush
#cycle()迭代器能够不停的反复依次产生'|/-\\'字符,其中第一个\为转义字符
for char in itertools.cycle('|/-\\'):
status = char + ' ' + msg
write(status)
#刷新输出
flush()
#uncoded'\x08'为空格标志,这里作用是使用空格清楚状态信息,把光标移回开头
write('\x08' * len(status))
try:
#执行这条语句是spin函数暂停等待asyncio.sleep()运行结束
yield from asyncio.sleep(.01)
except asyncio.CancelledError:
break
write(' '* len(status) + '\x08' * len(status))
@asyncio.coroutine
def supervisor():
#asyncio.async()方法会为协程spin()排定运行时间,即将其包装成一个Task对象
spinner = asyncio.async(spin('thinking!'))
#todo spin(msg),不断刷新旋转条
print('spinner object: ', spinner)
#当运行到yield from时,supervisor协程会停止运行,将控制权还给其调用者,只作为main函数
#和slow_down()函数的通信的管道,当slow_down()运行结束,supervisor函数恢复运行
result = yield from slow_down()
spinner.cancel()
return result
def main():
#获取事件循环的引用
loop = asyncio.get_event_loop()
#asyncio.run_until_complete()方法能够驱动supervisor函数运行直至结束
result = loop.run_until_complete(supervisor())
#关闭事件循环
loop.close()
print('Answer: ', result)
if __name__ == '__main__':
main()
4.运行过程
- 使用asyncio.get_event_loop()初始化一个支持异步调度的事件循环;
- 然后使用事件循环的run_until_complete()去驱动supervisor(),这里返回的result是一个已经对superviror()排定执行时间的Task对象;
- supervisor()中又利用asyncio.async()方法驱动协程spin,同样返回的spinner也是一个已经对spin()协程排定执行时间的Task对象
- 运行yield from slow_down()函数时supervisor是处于暂停状态,但是spinner和slow_down()都处于并发运行的状态,当slow_down()函数执行的等待3秒结束后,supervisor恢复运行,进而结束spinner协程。
- supervisor()协程运行结束,main()中的事件循环得到回调,因为这里事件循环中只有一个事件,于是得到回调的loop运行结束,接着关闭事件循环。
- 程序运行结束