Python 异步编程的工作原理如下:
事件监听
- asyncio 会设置各种事件监听器,比如网络 I/O、文件 I/O、定时器等。
- 当这些监听的事件发生时,asyncio 会将事件添加到事件队列中。
事件循环
- asyncio 的核心是一个事件循环,它会定期检查事件队列中是否有新的事件需要处理。
- 事件循环的检查频率通常由操作系统的调度策略决定,可能是 1ms、10ms 或更长。
事件分发
- 当事件队列中有新事件时,事件循环会从队列中取出事件,并分发给相应的事件处理函数。
- 事件处理函数会根据事件的类型和内容执行相应的异步操作。
yielding 切换
- 在事件处理函数中,如果遇到耗时的操作,可以使用 await 关键字让出 CPU 控制权。
- 这样事件循环就可以转而处理其他就绪的事件,提高整体的并发性和吞吐量。
资源管理
- 异步操作完成后,asyncio 会根据需要释放或分配相关的系统资源,比如网络连接、数据库连接等。
- 资源的管理同样是通过事件循环的定期检查来实现的。
可以看到,Python 异步编程的本质也是基于定期检查事件状态的时间驱动机制。这种机制可以帮助 Python 充分利用 CPU 资源,提高程序的并发性和响应速度,是 Python 异步编程的核心原理。
async
在 Python 中,async 关键字用于声明一个异步函数,它的执行逻辑如下:
函数声明
- 使用 async def 关键字声明一个异步函数。
- 异步函数内部可以使用 await 关键字来等待异步操作的完成。
函数调用
- 调用异步函数时,它会返回一个 coroutine 对象,而不是直接执行函数体内的代码。
- 这个 coroutine 对象可以被传递给 await 关键字,用于等待异步函数的执行完成。
事件循环
- 异步函数的执行需要依赖于事件循环(event loop)。
- 事件循环是一个无限循环,它会检查是否有就绪的异步任务,并依次执行它们。
任务的执行
- 当一个协程(coroutine)被传递给事件循环时,它会被封装成一个 Task 对象。
- 事件循环会检查 Task 对象的状态,如果它准备就绪,就会恢复协程的执行。
- 如果协程中有 await 语句,事件循环会暂停当前协程的执行,转而去执行其他就绪的协程。
错误处理
- 如果协程内部发生异常,该异常会被传播到调用者,可以使用 try-except 块进行处理。
下面是一个简单的示例,演示了 async 的执行逻辑:
import asyncio
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
print(f"started at {asyncio.get_running_loop().time()}")
await say_after(1, 'hello')
await say_after(2, 'world')
print(f"finished at {asyncio.get_running_loop().time()}")
asyncio.run(main())
在这个例子中:
- main() 函数是一个异步函数,它调用了两个异步函数 say_after()。
- 在 main() 函数中,await 关键字用于等待 say_after() 函数的执行完成。
- 当 main() 函数被传递给事件循环时,它会被封装成一个 Task 对象。
- 事件循环会检查 Task 对象的状态,如果它准备就绪,就会恢复 main() 函数的执行。
- 当遇到 await 时,当前协程(main)会暂停执行,事件循环会转而执行其他就绪的协程。
- 当 say_after() 函数执行完成时,事件循环会恢复 main() 函数的执行,并将结果赋值给被 await 的变量。
await
在 Python 中,await 关键字是用于处理异步操作的,它是 Python 3.5 引入的语法糖。await 被用于等待一个异步函数的执行结果。下面我们来详细了解一下 await 的执行逻辑。
异步函数的调用
- 当一个异步函数被调用时,它会立即返回一个 coroutine 对象,而不是直接执行函数体内的代码。
- 这个 coroutine 对象可以被传递给 await 关键字,用于等待异步函数的执行完成。
await 关键字的执行
- 当遇到 await 关键字时,当前协程会暂停执行,将控制权交给事件循环(event loop)。
- 事件循环会检查被 await 的对象是否已经准备好(ready)。如果准备好了,就会恢复当前协程的执行,继续执行后续代码。
- 如果被 await 的对象还没有准备好,事件循环会挂起当前协程,转而去执行其他就绪的协程,直到被 await 的对象准备好。
异步函数的返回值
- 当一个异步函数执行完成时,它会返回一个 Future 对象,表示异步操作的结果。
- 在 await 表达式中,这个 Future 对象的结果会被赋值给被 await 的变量。
错误处理
- 如果被 await 的对象发生异常,该异常会被传播到调用者,可以使用 try-except 块进行处理。
下面是一个简单的示例,演示了 await 的执行逻辑:
import asyncio
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
print(f"started at {asyncio.get_running_loop().time()}")
await say_after(1, 'hello')
await say_after(2, 'world')
print(f"finished at {asyncio.get_running_loop().time()}")
asyncio.run(main())
在这个例子中:
- main() 函数是一个异步函数,它调用了两个异步函数 say_after()。
- 在 main() 函数中,await 关键字用于等待 say_after() 函数的执行完成。
- 当遇到 await 时,当前协程(main)会暂停执行,事件循环会转而执行其他就绪的协程。
- 当 say_after() 函数执行完成时,事件循环会恢复 main() 函数的执行,并将结果赋值给被 await 的变量。