Python技术迷

Python 使用 Asyncio 进行异步 Python 编程

我们常常会遇到需要处理大量 I/O 操作的场景,比如网络请求、数据库查询、文件读写等。

传统的同步编程往往效率低下,因为每个任务都需要按顺序执行,这就导致了性能瓶颈,尤其是当某些操作需要等待很长时间时(比如等待网络响应或文件读取)。为了提升效率,异步编程应运而生,而 Python 的 asyncio 就是解决这一问题的利器。

同步编程 vs 异步编程

在我们开始深入了解 asyncio 之前,先来看看同步编程和异步编程的区别。同步编程是指任务按顺序一个接一个地执行,只有当前一个任务完成,后一个任务才会开始。举个例子,假设你在做三个任务:

  1. 打电话
  2. 吃饭
  3. 看电视

在同步编程中,你会先做一个任务,等完成了才做下一个任务。也就是说,如果打电话这件事需要等待很久(比如电话接通前你需要等待 5 分钟),那么你就得无所事事地等这 5 分钟才能开始吃饭,吃饭做完之后再去看电视。

而异步编程的思路是:你可以在等待某个操作完成的同时,去做其他事情。比如在上面的例子中,当你打电话时,如果接通前需要等待 5 分钟,你完全可以利用这 5 分钟时间去吃饭,吃完饭再去看电视,等电话接通时你就可以立即继续接电话,做完后再去做下一个任务。

为什么选择 Asyncio?

在 Python 中,asyncio 是处理异步编程的标准库,它通过引入“协程”概念,允许你编写非阻塞的代码,充分发挥 CPU 空闲时的能力,避免了传统阻塞 I/O 的性能瓶颈。

想象一下你在写一个爬虫,程序需要向多个网页发送请求,如果每个请求都要等它完成才能继续发送下一个,那速度简直慢得令人抓狂。使用 asyncio,你可以同时发送多个请求,而不用等待每个请求的响应。

Asyncio 基本概念

要理解 asyncio,首先需要了解两个基本概念:协程(Coroutine)和 事件循环(Event Loop)。

  • 协程:Python 中的协程是通过 async def 定义的函数,它在执行时会挂起并允许其他协程运行,直到它完成任务。
  • 事件循环:事件循环是一个不断运行的循环,用来调度协程的执行。当我们用 asyncio.run() 启动协程时,Python 会创建一个事件循环并开始执行。

编写第一个 Asyncio 程序

好了,开始动手编写异步程序吧!以下是一个简单的例子,展示了如何使用 asyncio 来执行异步任务。

import asyncio

# 定义一个模拟的异步 I/O 操作
asyncdefdo_work(task_name, delay):
    print(f"开始任务:{task_name}")
await asyncio.sleep(delay)  # 模拟 I/O 延迟
    print(f"完成任务:{task_name}")

# 主函数
asyncdefmain():
# 创建多个任务,异步执行
    task1 = asyncio.create_task(do_work("任务1", 3))
    task2 = asyncio.create_task(do_work("任务2", 2))
    task3 = asyncio.create_task(do_work("任务3", 1))

# 等待所有任务完成
await task1
await task2
await task3

# 运行事件循环
asyncio.run(main())

解释:

  1. async def do_work(task_name, delay):这是一个异步函数,它模拟执行任务时的延迟,使用 await asyncio.sleep(delay) 来模拟 I/O 操作的等待。
  2. asyncio.create_task():用来创建并调度协程任务。它会立即启动任务并返回一个任务对象。
  3. await task1:等待 task1 完成后,才会继续执行 task2 和 task3,这样我们就能够并发执行多个任务。

输出:

开始任务:任务1
开始任务:任务2
开始任务:任务3
完成任务:任务3
完成任务:任务2
完成任务:任务1

注意,虽然我们用 await 等待每个任务完成,但任务 2 和任务 3 并没有等任务 1 完成,它们是并发执行的。

异步编程的优势

1. 提高程序性能

异步编程让程序可以在等待 I/O 操作时,不浪费 CPU 资源,而是执行其他任务。这样,我们可以有效提高程序的吞吐量。

2. 适用于 I/O 密集型任务

异步编程特别适用于需要进行大量 I/O 操作的任务,比如爬虫、网络请求、文件操作等。在这些任务中,I/O 操作往往会占据大部分时间,异步编程能充分利用这些空闲时间来执行其他任务。

3. 节省内存和资源

在异步编程中,任务是通过协程的方式执行的,不需要为每个任务创建一个新的线程,因此比线程池的开销要小。这样可以减少内存使用,避免资源浪费。

结合实际应用:爬虫的异步实现

来个更实际的例子,假设你要实现一个爬虫,爬取多个网页的数据。如果你用同步方式,每个网页请求都得等到上一个完成才行,效率自然就慢了。那如何利用 asyncio 提高效率呢?下面是一个爬虫的异步实现示例:

import asyncio
import aiohttp

asyncdeffetch_page(url):
asyncwith aiohttp.ClientSession() as session:
asyncwith session.get(url) as response:
returnawait response.text()

asyncdefmain():
    urls = ["https://www.example.com", "https://www.example.org", "https://www.example.net"]
    tasks = [fetch_page(url) for url in urls]
    pages = await asyncio.gather(*tasks)

for page in pages:
        print(f"抓取到页面,长度为:{len(page)}")

asyncio.run(main())

解释:

  • 使用 aiohttp 库来异步地获取网页内容。通过 async with aiohttp.ClientSession() 创建一个异步会话,并在其中通过 session.get() 异步地获取网页。
  • asyncio.gather(*tasks):将多个异步任务并发执行,并等待所有任务完成。
  • 这个程序可以同时向多个网站发起请求,而无需等待每个请求的响应,极大提高了爬虫的效率。

面试题的最优回答

面试题:什么是异步编程,Python 中如何实现异步编程?

最优答案:

“异步编程是一种程序执行模型,能够让程序在执行某个任务时,不需要等待任务完成就可以执行其他任务,通常用于 I/O 密集型操作。在 Python 中,asyncio 是用来实现异步编程的标准库,它通过协程(async def)和事件循环(asyncio.run())来实现异步任务的调度。通过 asyncio.create_task(),我们可以并发执行多个协程,从而提高程序的性能,尤其在需要处理大量 I/O 操作时,异步编程能够充分利用等待时间,提高系统的响应速度和吞吐量。”

异步编程虽然学习曲线较陡,但掌握了它,可以让你在处理大量网络请求、数据库操作时,提升程序的执行效率,避免因为 I/O 阻塞而造成的性能瓶颈。

最后,我为大家打造了一份deepseek的入门到精通教程,完全免费:https://www.songshuhezi.com/deepseek

也可以看我写的这篇文章《DeepSeek满血复活,直接起飞!》来进行本地搭建。

对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
🔥虎哥私藏精品 热门推荐🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。

资料包含了《IDEA视频教程》、《最全python面试题库》、《最全项目实战源码及视频》及《毕业设计系统源码》,总量高达650GB,全部免费领取