王剑编程网

分享专业编程知识与实战技巧

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

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


三、在 asyncio 中处理错误

在处理异步代码时,异常仍然可能发生。你可以在协程中使用 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 的基础和高级特性,从简单的协程和任务调度到处理超时和同步。通过理解这些概念,你可以构建能够并发处理多个任务的高效应用程序。


#我的宝藏兴趣#

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言