Python asyncio异步编程核心机制与实战指南

一只小鹿
一只小鹿
发布时间:2026-10-15| 3人浏览过
原创

本文深入解析Python中asyncio异步编程的核心原理。从协程的概念出发,对比多线程与协程的优劣,介绍greenlet、gevent及asyncio的发展脉络。重点讲解async/await关键字的使用、事件循环机制、Task对象管理以及IO密集型场景下的性能优化策略,帮助开发者掌握高效的并发编程技巧。

1. 理解asyncio的基础:协程概念

协程的核心意义在于优化资源利用。

  1. 对于纯计算型操作,使用协程进行切换并无实际收益,因为保存和恢复状态反而会增加开销,降低执行效率。
  2. 针对IO型操作,协程的优势得以体现:在等待IO完成的间隙,程序可以切换去执行其他任务;当IO操作结束后,系统自动回调继续执行。这种机制大幅节省了系统资源并提升了性能,实现了真正的异步编程——即无需等待前一个任务结束即可执行后续代码。

2. 协程与多线程的异同分析

两者的共同点在于:

它们都属于并发操作模型。在同一时刻,多线程中只有一个线程处于执行状态,同样,协程在同一时刻也只有一个任务在执行。

主要区别如下:

多线程依赖操作系统在IO阻塞时切换线程以实现并发。切换时机由操作系统决定,开发者无需干预,但容易引发竞争条件(race condition)。

协程仅占用单个线程,在IO阻塞时通过手动切换任务来实现并发。切换时机由开发者控制,因此避免了竞争条件。此外,协程的任务切换开销远小于多线程的线程切换。从代码编写角度看,多线程并发的实现通常比协程更直观简便。

通常情况下,协程并发的处理效率高于多线程并发。

3. 使用greenlet实现协程

greenlet库用于创建协程,并通过switch函数实现协程间的切换。在某个协程执行过程中,可随时被其他协程通过switch打断,转而执行目标协程。原协程的中断现场会被保存,一旦再次获得CPU执行权,便恢复现场并从中断处继续执行。这种机制下的协程是同步执行的,无法实现真正的并发。

安装命令:pip install greenlet


import time
import greenlet
 
 
def func1():
  print("func11")
  gr2.switch()
  time.sleep(1)
  print("func22")
  gr2.switch()
 
 
def func2():
  print("func33")
  gr1.switch()
  time.sleep(1)
  print("func44")
 
 
start = time.time()
gr1 = greenlet.greenlet(func1)
gr2 = greenlet.greenlet(func2)
gr1.switch()
end = time.time()
print(end - start)

4. 使用yield关键字实现协程


def func1():
  yield 1
  yield from func2()
  yield 3
 
 
def func2():
  yield 2
  yield 4
 
 
ff = func1()
for item in ff:
  print(item)

5. 使用gevent库实现协程

(1)gevent基础协程实现

安装命令:pip install gevent


from greenlet import greenlet
from time import sleep
def func1():
  print("协程1")
  sleep(2)
  g2.switch()
  print("协程1恢复运行")
 
def func2():
  print("协程2")
  sleep(1)
  g3.switch()
def func3():
  print("协程3")
  sleep(1)
  g1.switch()
 
if __name__ == '__main__':
  # 使用greenlet来创建三个协程
  g1 = greenlet(func1)
  g2 = greenlet(func2)
  g3 = greenlet(func3)
  # print(g1)
  g1.switch() # 让协程g1取抢占cpu资源

(2)gevent实现异步协程


# 协程被创建出来以后默认是多个协程同步执行
# 我们可以加入monkey补丁,把同步的协程转成异步协程
from gevent import monkey # 注意:monkey的引入必须在其他模块之前
 
monkey.patch_all() # 用monkey给整个协程队列,添加一个非阻塞I/O的补丁,使得他们成为异步协程
import time
import requests
import gevent
 
headers = {
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
 
 
def func(url, i):
  print("协程%d开启!" % i)
  res = requests.get(url=url, headers=headers)
  html = res.text
  print("协程%d执行结束,获取到的响应体大小为:%d" % (i, len(html)))
 
 
if __name__ == '__main__':
  start = time.time()
  urls = [
    "https://www.baidu.com/",
    "https://www.qq.com/",
    "https://www.sina.com.cn",
    "https://www.ifeng.com/",
    "https://www.163.com/"
  ]
  # 创建5个协程分别对上面5个网站进行访问
  g_list = []
  for i in range(len(urls)):
    g = gevent.spawn(func, urls[i], i)
    g_list.append(g)
    # func(urls[i], i)
  gevent.joinall(g_list)
  end = time.time()
  print(end - start)

6. 使用asyncio模块实现异步协程

该模块适用于Python 3.4及以上版本。asyncio的强大之处在于,当遇到IO操作时,它会自动切换执行其他任务,无需手动干预。


import time
import asyncio
 
 
@asyncio.coroutine
def func1():
  print(1)
  yield from asyncio.sleep(1) # 遇到IO耗时操作,自动切换到tasks中的其它任务
  print(2)
 
 
@asyncio.coroutine
def func2():
  print(3)
  yield from asyncio.sleep(1) # 遇到IO耗时操作,自动切换到tasks中的其它任务
  print(4)
 
 
tasks = [
  asyncio.ensure_future(func1()),
  asyncio.ensure_future(func2())
]
 
start = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print(end - start)

7. 使用async与await关键字实现异步编程(当前推荐方式)

该语法结构自Python 3.5版本起可用。


import time
import asyncio
 
 
async def func1():
  print(1)
  await asyncio.sleep(1)
  print(2)
 
 
async def func2():
  print(3)
  await asyncio.sleep(1)
  print(4)
 
 
tasks = [
  asyncio.ensure_future(func1()),
  asyncio.ensure_future(func2())
]
 
start = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print(end - start)

7.1 事件循环机制

事件循环可视为一个while循环,它周期性运行并执行一系列任务,在满足特定条件时终止循环。

伪代码逻辑如下:


# 伪代码
任务列表 = [ 任务1, 任务2, 任务3,... ]
while True:
  可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回
  for 就绪任务 in 已准备就绪的任务列表:
    执行已就绪的任务
  for 已完成的任务 in 已完成的任务列表:
    在任务列表中移除 已完成的任务
  如果 任务列表 中的任务都已完成,则终止循环

7.2 协程与异步编程基础

协程函数的定义形式为 async def。

协程对象是指调用协程函数后返回的对象实例。


# 定义一个协程函数
async def func():
  pass
# 调用协程函数,返回一个协程对象
result = func()

注意:调用协程函数时,函数内部的代码并不会立即执行,而是直接返回一个协程对象。

7.3 基本应用方法

在程序中,若要执行协程函数内部的代码,必须配合使用 事件循环 和 协程对象,例如:


import asyncio
async def func():
  print("协程内部代码")
# 调用协程函数,返回一个协程对象。
result = func()
# 方式一
# loop = asyncio.get_event_loop() # 创建一个事件循环
# loop.run_until_complete(result) # 将协程当做任务提交到事件循环的任务列表中,协程执行完成之后终止。
# 方式二
# 本质上方式一是一样的,内部先 创建事件循环 然后执行 run_until_complete,一个简便的写法。
# asyncio.run 函数在 Python 3.7 中加入 asyncio 模块,
asyncio.run(result)

这一过程可简单理解为:将协程作为任务添加到 事件循环 的任务列表中,随后事件循环检测列表中的协程是否已准备就绪(默认为就绪状态)。若就绪,则执行其内部代码。

7.4 await关键字详解

await是仅在协程函数中使用的关键字。当遇到IO操作时,它用于挂起当前协程(任务)。在挂起期间,事件循环可以执行其他协程(任务)。当当前协程的IO处理完成后,程序再次切换回来执行await之后的代码。

await后需跟随可等待对象(包括协程对象、Future对象或Task对象)。

示例1:await与协程对象配合


import asyncio
 
 
async def func1():
  print("start")
  await asyncio.sleep(1)
  print("end")
  return "func1执行完毕"
 
 
async def func2():
  print("func2开始执行")
  # await关键字后面可以跟可等待对象(协程对象、Future对象、Task对象)
  response = await func1()
  print(response)
  print("func2执行完毕")
 
 
asyncio.run(func2())

示例2:在协程函数中可多次使用await关键字


import asyncio
 
 
async def func1():
  print("start")
  await asyncio.sleep(1)
  print("end")
  return "func1执行完毕"
 
 
async def func2():
  print("func2开始执行")
  # await关键字后面可以跟可等待对象(协程对象、Future对象、Task对象)
  response = await func1()
  print(response)
  response2 = await func1()
  print(response2)
  print("func2执行完毕")
 
 
asyncio.run(func2())

7.5 Task对象管理

Tasks用于并发调度协程。通过asyncio.create_task(协程对象)方式创建Task对象,可使协程加入事件循环等待调度执行。除使用 asyncio.create_task() 函数外,还可使用低层级的 loop.create_task() 或 ensure_future() 函数。官方不建议手动实例化Task对象。

其本质是将协程对象封装为Task对象,并立即将其加入事件循环,同时追踪协程的运行状态。

注意:asyncio.create_task() 函数于Python 3.7版本引入。在Python 3.7之前,可改用低层级的 asyncio.ensure_future() 函数。

示例1:


import asyncio
 
 
async def func():
  print(1)
  await asyncio.sleep(1)
  print(2)
  return "func的返回值"
 
 
async def main():
  print(3)
  # 创建协程,将协程封装到一个task对象中并立即添加到事件循环列表中,等待事件循环去执行,(默认是就绪状态)
  task1 = asyncio.create_task(func())
  # 创建协程,将协程封装到一个task对象中并立即添加到事件循环列表中,等待事件循环去执行,(默认是就绪状态)
  task2 = asyncio.create_task(func())
  # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
  # 此处的await是等待相对应的协程全都执行完毕并获取结果
  ret1 = await task1
  ret2 = await task2
  print(ret1, ret2)
 
 
asyncio.run(main())

示例2:此用法较为常见


import asyncio
 
 
async def func():
  print(1)
  await asyncio.sleep(1)
  print(2)
  return "func的返回值"
 
 
async def main():
  print(3)
  # 创建协程,将协程封装到Task对象中并添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
  # 在调用
  task_list = [
    asyncio.create_task(func()),
    asyncio.create_task(func())
  ]
  # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
  # 此处的await是等待所有协程执行完毕,并将所有协程的返回值保存到done
  # 如果设置了timeout值,则意味着此处最多等待的秒,完成的协程返回值写入到done中,未完成则写到pending中。
  done, pending = await asyncio.wait(task_list, timeout=None)
  print(done)
  print(pending)
 
 
asyncio.run(main())

示例3:


import asyncio
 
 
async def func():
  print("执行协程函数内部代码")
  # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。
  response = await asyncio.sleep(2)
  print("IO请求结束,结果为:", response)
 
 
coroutine_list = [func(), func()]
# 错误:coroutine_list = [ asyncio.create_task(func()), asyncio.create_task(func()) ]
# 此处不能直接 asyncio.create_task,因为将Task立即加入到事件循环的任务列表,
# 但此时事件循环还未创建,所以会报错。
# 使用asyncio.wait将列表封装为一个协程,并调用asyncio.run实现执行两个协程
# asyncio.wait内部会对列表中的每个协程执行ensure_future,封装为Task对象。
done, pending = asyncio.run(asyncio.wait(coroutine_list))

总结:

在程序中,只要看到async和await关键字,即表明内部基于协程实现了异步编程。这种异步编程通过单线程在IO等待期间执行其他任务来实现并发。

场景选择建议: 若为IO密集型且IO请求耗时较长,建议使用协程。 若为IO密集型且IO请求响应较快,建议使用多线程。 若为计算密集型,建议利用多核CPU,采用多进程方案。

以上为Python中asyncio异步编程的详细解析,更多关于Python使用asyncio的资料,请持续关注本站其他相关文章!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。