Python 中的 asyncio 编程入门示例-2

360影视 动漫周边 2025-05-27 07:50 3

摘要:Python 的 asyncio 库是用于编写并发代码的,它使用 async/await 语法。它为编写异步程序提供了基础,通过非阻塞调用高效处理 I/O 密集型操作,适用于涉及网络连接、文件 I/O 或数据库的程序。本教程将介绍 asyncio 的基础知识,

Python 的 asyncio 库是用于编写并发代码的,它使用 async/await 语法。它为编写异步程序提供了基础,通过非阻塞调用高效处理 I/O 密集型操作,适用于涉及网络连接、文件 I/O 或数据库的程序。本教程将介绍 asyncio 的基础知识,并逐步从简单示例过渡到更复杂的示例。

asyncio.wait_for 允许你为协程设置超时时间。如果协程在指定时间内未完成,则会引发 TimeoutError。

示例:使用 asyncio.wait_for

import asyncio # 导入 asyncio 模块,提供异步编程支持# 定义一个长时间运行的协程任务async def long_running_task: # 模拟耗时操作,暂停协程执行 5 秒 # 此时协程会释放控制权,但不会阻塞事件循环 await asyncio.sleep(5) return "Task completed" # 返回任务完成的消息# 定义主协程函数async def main: try: # 使用 asyncio.wait_for 执行协程,并设置 3 秒超时 # 如果任务在 3 秒内未完成,将引发 TimeoutError 异常 result = await asyncio.wait_for(long_running_task, timeout=3) print(result) # 如果任务在超时前完成,打印任务结果 except asyncio.TimeoutError: # 捕获超时异常 # 当长时间运行的任务超过了 3 秒的超时限制时,会执行此代码块 print("Task timed out!") # 打印超时消息# 使用 asyncio.run 运行主协程# 这会创建事件循环、运行协程,然后关闭事件循环asyncio.run(main)

输出:Task timed out!

解释:

该任务需要 5 秒才能完成,但 wait_for 的超时时间设置为 3 秒,因此会引发超时错误。

asyncio 提供了多种同步原语,用于协调多个协程,包括:

锁(locks):防止多个协程同时访问共享资源。事件(Event):当某个事件发生时,向一个或多个协程发送信号。

示例:使用 asyncio.Lock

import asyncio # 导入 asyncio 模块,提供异步编程支持# 创建一个全局的异步锁对象# 锁是一种同步原语,用于确保一次只有一个协程可以访问共享资源lock = asyncio.Lock# 定义一个访问共享资源的异步函数async def access_shared_resource(task_id): # 使用异步上下文管理器获取锁 # 当一个协程获取锁时,其他尝试获取同一个锁的协程将被挂起,直到锁被释放 # async with 语法确保即使发生异常,锁也会被正确释放 async with lock: print(f"Task {task_id} is accessing the shared resource") # 打印任务开始访问共享资源的消息 # 模拟处理共享资源的耗时操作 await asyncio.sleep(2) print(f"Task {task_id} is done") # 打印任务完成的消息 # 离开 async with 代码块时,锁会自动释放,允许其他等待的协程获取锁# 定义主协程函数async def main: # 使用 asyncio.gather 并发执行多个协程任务 # 尽管这些任务是并发调度的,但由于它们使用相同的锁, # 所以实际上会按顺序执行 access_shared_resource 函数的内部代码 # 第二个任务必须等待第一个任务释放锁后才能继续执行 await asyncio.gather(access_shared_resource(1), access_shared_resource(2))# 使用 asyncio.run 运行主协程# 这会创建事件循环、运行协程,然后关闭事件循环asyncio.run(main)

输出:

Task 1 is accessing the shared resource

Task 1 is done

Task 2 is accessing the shared resource

Task 2 is done

在处理异步代码时,异常仍然可能发生。你可以在协程中使用 try/except 块来处理它们。

示例:处理异常

import asyncio # 导入 asyncio 模块,提供异步编程支持# 定义一个可能引发错误的协程任务async def faulty_task: # 模拟 I/O 绑定任务,暂停协程执行 1 秒 await asyncio.sleep(1) # 主动引发一个 ValueError 异常 raise ValueError("An error occurred!") # 抛出错误消息# 定义主协程函数async def main: try: # 调用可能引发错误的协程任务 await faulty_task except ValueError as e: # 捕获 ValueError 异常并处理 print(f"Caught an error: {e}") # 打印捕获的错误消息# 使用 asyncio.run 运行主协程# 这会创建事件循环、运行协程,然后关闭事件循环asyncio.run(main)

输出:Caught an error: An error occurred!

让我们把所有内容整合起来,用 aiohttp 构建一个简单的网络爬虫,用于异步 HTTP 请求。

示例:使用 asyncio 和 aiohttp 的网络爬虫

import asyncio # 导入 asyncio 模块,用于异步编程import aiohttp # 导入 aiohttp 模块,用于异步 HTTP 请求# 定义一个异步函数,用于获取指定 URL 的内容async def fetch_url(session, url): # 使用异步上下文管理器发送 GET 请求 async with session.get(url) as response: # 等待并返回响应的文本内容 return await response.text# 定义主协程函数async def main: # 定义要抓取的 URL 列表 urls = [ "https://baidu.com", # 示例 URL "https://pypi.org", # 示例 URL "https://python.org" # 示例 URL ] # 使用异步上下文管理器创建一个 aiohttp 客户端会话 async with aiohttp.ClientSession as session: # 为每个 URL 创建一个抓取任务 tasks = [fetch_url(session, url) for url in urls] # 并发执行所有抓取任务,并等待所有任务完成 responses = await asyncio.gather(*tasks) # 遍历 URL 和对应的响应内容 for url, response in zip(urls, responses): # 打印 URL 和响应内容的长度 print(f"URL: {url} - 响应内容的长度: {len(response)} 字符数")# 使用 asyncio.run 运行主协程# 这会创建事件循环、运行协程,然后关闭事件循环asyncio.run(main)

输出:

解释:

这个示例使用 aiohttp 异步获取多个 URL 的内容。使用 asyncio.gather 并发收集响应,这比顺序请求要快得多。

Asyncio 是 Python 中用于编写并发代码的强大库,非常适合处理 I/O 密集型和高级结构化的网络代码。我们已经涵盖了 asyncio 的基础和高级特性,从简单的协程和任务调度到处理超时和同步。通过理解这些概念,你可以构建能够并发处理多个任务的高效应用程序。

来源:软件架构

相关推荐