8. asyncio三种执行协程的机制:
- 使用asyncio.run()执行协程。一般用于执行最顶层的入口函数,如main()。
- await一个协程。一般用于在一个协程中调用另一协程
如下是一个示例:
#!/usr/bin/python import time import asyncio async def say_after(delay,what): await asyncio.sleep(delay) print(what) async def main(): print(f"started at {time.strftime('%X')}") await say_after(1,"hello") await say_after(2,"world") print(f"finished at {time.strftime('%X')}") asyncio.run(main())
输出:
$ python3 ns6.py started at 11:42:48 hello world finished at 11:42:51
执行耗时 3秒
- 用
asyncio.create_task()
方法将Coroutine(协程)封装为Task(任务)。一般用于实现异步并发操作。
需要注意的是,只有在当前线程存在事件循环的时候才能创建任务(Task)。
我们修改以上的例程,并发执行 两个say_after协程。
#!/usr/bin/python import time import asyncio async def say_after(delay,what): await asyncio.sleep(delay) print(what) async def main(): task1 = asyncio.create_task(say_after(1,"hello")) task2 = asyncio.create_task(say_after(2,"world")) print(f"started at {time.strftime('%X')}") await task1 await task2 print(f"finished at {time.strftime('%X')}") asyncio.run(main())
输出:
$ python3 ns7.py started at 14:22:09 hello world finished at 14:22:11
耗时2秒
9. “可等待”对象(Awaitables)
如果一个对象能够被用在await表达式中,那么我们称这个对象是可等待对象(awaitable object)。很多asyncio API都被设计成了可等待的。
主要有三类可等待对象:
协程coroutine
任务Task
未来对象Future。
10. Coroutine(协程)
Python的协程是可等待的(awaitable),因此能够被其他协程用在await表达式中。
import asyncio import time async def nested(): print("something") async def main(): # 如果直接调用 "nested()",什么都不会发生. # 直接调用的时候只是创建了一个 协程对象 ,但这个对象没有被 await, # 所以它并不会执行. nested() print(f"finished at {time.strftime('%X')}") # 那么我们 await 这个协程,看看会是什么结果: await nested() # 将会打印 "something". asyncio.run(main())
输出:
as8.py:11: RuntimeWarning: coroutine 'nested' was never awaited nested() RuntimeWarning: Enable tracemalloc to get the object allocation traceback finished at 14:28:28 something
术语coroutine或协程指代两个关系紧密的概念:
- 协程函数(coroutine function):由async def定义的函数;
- 协程对象(coroutine object):调用 协程函数返回的对象。
asyncio也支持传统的基于生成器的协程。
11. Task(任务)
Task
用来 并发的 调度协程。
当一个协程通过类似 asyncio.create_task()
的函数被封装进一个 Task时,这个协程 会很快被自动调度执行
import asyncio async def nested(): print("something") return 42 async def main(): # Schedule nested() to run soon concurrently # with "main()". task = asyncio.create_task(nested()) # "task" can now be used to cancel "nested()", or # can simply be awaited to wait until it is complete: await task asyncio.run(main())
输出:
$ python3 ns8.py something
12. Future(未来对象)
Future 是一种特殊的 底层 可等待对象,代表一个异步操作的最终结果。
当一个Future对象被await的时候,表示当前的协程会持续等待,直到 Future对象所指向的异步操作执行完毕。
在asyncio中,Future对象能使基于回调的代码被用于asyn/await表达式中。
一般情况下,在应用层编程中,没有必要 创建Future对象。
有时候,有些Future对象会被一些库和asyncio API暴露出来,我们可以await它们:
13. 执行asyncio程序
asyncio.run(coro, * , debug=False)
这个函数运行coro参数指定的 协程,负责 管理asyncio事件循环 , 终止异步生成器。
在同一个线程中,当已经有asyncio事件循环在执行时,不能调用此函数。
如果debug=True,事件循环将运行在 调试模式。
此函数总是创建一个新的事件循环,并在最后关闭它。建议将它用作asyncio程序的主入口,并且只调用一次。
Python3.7新增
重要:这个函数是在Python3.7被临时添加到asyncio中的。
参考:
Async IO in Python: A Complete Walkthrough
Python Asyncio Part 1 – Basic Concepts and Patterns
Asyncio: An Introduction
Asyncio: Understanding Async / Await in Python
Python Asynchronous Programming - AsyncIO & Async/Await