Python 中理解 asyncio 的极简入门-1

内容分享2个月前发布
0 1 0

关于 asyncio 的文章和课程已经有许多了,为什么还要再写一篇呢?这篇文章的目标是用简单直接的方式解释 Python 中的异步编程概念。本文通过简单的示例探索 Python 的 asyncio API,协助开发者快速上手。

Python 中理解 asyncio 的极简入门-1

我在自己的项目中使用了 asyncio,并且很喜爱它用很少的代码就能实现并发的方式。希望你也会有同样的感受。

一、什么是 asyncio?

Python 的 asyncio 是一种基于协程的并发模型,它提供了一种优雅的构造,用于编写并发 Python 代码,而无需使用线程。设计并发解决方案的思维方式与传统的基于线程的方法不同。我们知道,线程(threading)主要用于处理 I/O 密集型任务,而多进程(multiprocessing)则适用于 CPU 密集型任务。

如果你曾经用 JavaScript 编程,所有的 ES6 和 Node.js 程序都在事件循环中执行。Python 通过允许开发者创建自定义循环以及在这些循环上调度函数,实现了一样的功能。

Python 的 asyncio 标准库允许开发者在开发并发程序时使用 async/await 语法。

asyncio 一般超级适合用于 I/O 密集型和高结构化的网络代码。

常见的 I/O 密集型处理用例包括:

  • 指标收集系统
  • 聊天室
  • 实时在线游戏
  • Python 中的流式处理

二、asyncio 的基础

一般,Python 程序通过运行函数来处理输入值。类似地,在 asyncio 中,需要创建一种特殊的函数,称为协程(co-routines)使普通函数成为协程的关键是 async 关键字。让我们来看一个简单的加法函数,它将两个整数相加并返回结果。

# A normal function
def add(x: int, y: int):
 return x + y

# A co-routine
async def add(x: int, y: int):
 return x + y

当你用 async 关键字将一个函数标记为特殊函数时,事件循环就可以将这个函数作为协程(co-routines)执行。

import asyncio

# A co-routine
async def add(x: int, y: int):
 return x + y

# An event loop
loop = asyncio.get_event_loop()

# Pass the co-routine to the loop
result = loop.run_until_complete(add(3, 4))
print(result) # Prints 7

在代码片段中,我们通过调用 asyncio.get_event_loop 方法创建一个新的事件循环。它返回一个可以执行协程的新循环。run_until_complete 方法隐含了三件事:

  • 启动循环
  • 执行作为参数传递的协程(co-routines)
  • 停止循环

run_until_complete 方法返回协程完成后的值。这是一个创建协程并在事件循环上调度它的简单示例。

Python 中理解 asyncio 的极简入门-1

在下一节中,我们将看到如何调度多个协程。

三、在不同循环中调度多个协程

如果我们多次调用 run_until_complete,这相当于以同步方式运行事件循环两次。因此,在下面的程序中,result1 和 result2 是同步发生的。

import asyncio # 导入 asyncio 模块,它提供了异步编程的基础设施

# 定义一个协程函数
# 协程是一种特殊的函数,可以在执行过程中暂停并稍后恢复
async def add(x: int, y: int):
 return x + y # 简单的加法运算

# 创建事件循环
# 事件循环是异步编程的核心,它管理和调度所有协程的执行
# 注意:这种方式在较新版本的 Python 中已被弃用,提议使用 asyncio.run()
loop = asyncio.get_event_loop()

# 依次执行两个协程
# run_until_complete() 方法接收一个协程并阻塞运行,直到协程执行完毕
result1 = loop.run_until_complete(add(3, 4))
result2 = loop.run_until_complete(add(5, 5))

在程序中,事件循环启动,然后执行第一个协程并停止。这个过程会重复执行第二个协程。这类似于两个函数依次运行的普通同步调用。处理过程如图所示:

Python 中理解 asyncio 的极简入门-1

四、在同一个循环中调度多个协程

如果我们需要将多个协程作为一批异步地调度到同一个事件循环上,可以这样做:

import asyncio # 导入 asyncio 模块,提供异步编程支持

# 定义一个协程函数
# 协程是可以在执行过程中暂停并稍后恢复的特殊函数
async def add(x: int, y: int):
 return x + y # 简单的加法运算,返回两个数的和

# 获取事件循环
# 注意:这种方式在较新版本的 Python 中已被弃用,提议使用 asyncio.run()
loop = asyncio.get_event_loop()

# 创建一个调度协程的函数
# 这个函数本身也是一个协程,可以使用 await 等待其他协程完成
async def get_results():
 # 使用 await 关键字等待 add 协程完成并获取结果
 # await 会暂停当前协程的执行,直到被等待的协程返回结果
 result1 = await add(3, 4)
 result2 = await add(5, 5)

 print(result1, result2) # 打印结果:7 10
 loop.stop() # 停止事件循环的运行

# 将 get_results 协程作为任务添加到事件循环中
# create_task 会立即返回,不会等待协程完成
# 任务会在事件循环运行时被调度执行
loop.create_task(get_results())

# 阻塞调用,会一直运行直到被 loop.stop() 中断
# run_forever() 会持续运行事件循环,直到显式调用 stop() 方法
try:
 loop.run_forever()
finally:
 # 无论事件循环如何结束(正常退出或发生异常),都确保关闭事件循环
 # close() 会释放事件循环占用的资源,是良好的编程习惯
 loop.close()

上面的算法很简单:

  • 获取事件循环
  • 使用 loop.run_forever 方法运行事件循环。该行会阻塞,直到有人调用 loop.stop 方法。一旦在未来调用了 loop.stop 方法,loop.close 将停止事件循环。
  • 从协程 get_results 创建一个任务,并将其调度到事件循环上。
  • 当 get_results 执行时,它使用 await 关键字将另外两个协程调度到循环上。await 语句会立即运行协程并返回结果。
  • 最后,在获取两个协程的结果后,我们停止事件循环(stop)。

整个过程如下图所示:

Python 中理解 asyncio 的极简入门-1

下一篇文章继续探讨 asyncio API 的开发实践,包括 asyncio 库提供的包装方法 asyncio.run()、协程与任务的区别等等。

#我的宝藏兴趣#

© 版权声明

相关文章

1 条评论

  • 头像
    丢失时间的鱼 读者

    向你学习👍

    无记录
    回复