Python异步编程:asyncio和aiofiles

asyncio

asyncio 是 Python 的标准库之一,用于编写单线程的并发代码。它使用协程(coroutines)和事件循环(event loop)来实现异步编程。协程是 Python 中一种特殊的函数,可以使用 async def 定义,并通过 await 表达式来挂起和恢复执行。

假设我们有两个任务:下载网页内容和计算数字的平方。在异步编程中,我们可以同时启动这两个任务,而不需要等待一个任务完成后再开始另一个。这样,我们就可以更有效地利用资源,特别是当 I/O 操作(如网络请求)是性能瓶颈时。

下面是一个使用 asyncio 的示例:

import asyncio  
import aiohttp  
import time  
  
# 模拟下载网页内容的异步函数  
async def fetch(session, url):  
    async with session.get(url) as response:  
        return await response.text()  
  
# 模拟计算数字平方的异步函数(注意:这个示例中我们并没有真正使其异步,但为了演示,我们假装它是)  
async def compute(x):  
    # 假装我们在这里做了一些耗时的计算  
    time.sleep(1)  # 注意:在异步代码中应该使用 await asyncio.sleep(1) 来避免阻塞事件循环  
    return x * x  
  
async def main():  
    # 获取异步HTTP会话  
    async with aiohttp.ClientSession() as session:  
        # 发起异步下载任务  
        download_task = asyncio.create_task(fetch(session, 'http://example.com'))  
          
        # 发起异步计算任务  
        compute_task = asyncio.create_task(compute(10))  
  
        # 等待两个任务完成  
        # 注意:这里我们使用 await 来等待两个任务同时完成,但实际上它们是并发执行的  
        html = await download_task  
        square = await compute_task  
  
        # 打印结果  
        print('Downloaded:', len(html))  
        print('Square:', square)  
  
# 运行异步主函数  
asyncio.run(main())  
  
# 注意:上面的 compute 函数中使用了 time.sleep(1),这在异步编程中通常是不推荐的,  
# 因为它会阻塞整个事件循环。正确的做法是使用 await asyncio.sleep(1)。  
# 但为了简单起见,我在这里保留了 time.sleep,并指出在异步函数中应该避免这样做。

然而,请注意,上面的 compute 函数实际上并没有进行真正的异步操作,它使用了 time.sleep(1) 来模拟耗时计算,这实际上会阻塞事件循环。在异步编程中,我们应该使用 await asyncio.sleep(1) 来避免阻塞。

让我们修改 compute 函数以使用 asyncio.sleep

import asyncio  
  
# 修改后的 compute 函数  
async def compute(x):  
    # 使用 asyncio.sleep 来避免阻塞事件循环  
    await asyncio.sleep(1)  
    return x * x  
  
# ... 其余代码保持不变 ...
现在,compute函数和 fetch函数都是异步的,它们可以并发执行,而不会相互阻塞。当 asyncio.run(main())被调用时,Python 会创建一个事件循环,并在该循环中并发地执行 fetch 和 compute任务。

aiofiles

aiofiles 是一个第三方库,它提供了异步的文件操作接口。这意味着你可以在不阻塞事件循环的情况下读写文件。这对于需要处理大量文件 I/O 的异步应用程序特别有用。

在 Python 的异步编程模型中,asyncio 是核心库,但标准库中的文件操作(如 open()read()write() 等)并不是异步的。aiofiles 填补了这一空白,提供了异步版本的这些操作。

以下是一个使用 aiofiles 进行异步文件操作的简单示例。在这个例子中,我们将异步地读取一个文件的内容,并打印出来。

首先,你需要安装 aiofiles。如果你还没有安装,可以通过 pip 安装:

pip install aiofiles
然后,你可以编写如下的 Python 脚本:
import asyncio  
import aiofiles  
  
async def read_file(filepath):  
    # 使用 aiofiles.open 异步打开文件  
    async with aiofiles.open(filepath, mode='r') as f:  
        # 异步读取文件内容  
        contents = await f.read()  
        return contents  
  
async def main():  
    # 假设我们有一个名为 "example.txt" 的文件  
    filepath = 'example.txt'  
    # 异步读取文件  
    contents = await read_file(filepath)  
    # 打印文件内容  
    print(contents)  
  
# 运行异步主函数  
asyncio.run(main())
在这个例子中,read_file函数是一个异步函数(由 async def定义),它使用 aiofiles.open
 异步地打开文件。aiofiles.open的用法与内置的 open
 函数非常相似,但它返回一个异步文件对象,你可以在这个对象上调用异步方法,如 read()

main 函数也是异步的,它调用了 read_file 函数并打印了文件的内容。最后,我们使用 asyncio.run(main()) 来运行异步的主函数。

实际上,aiofiles 库是设计来与 asyncio 库一起使用的,因为 aiofiles 提供了基于 asyncio 的异步文件操作接口。没有 asyncio,aiofiles 就无法正常工作,因为 aiofiles 的所有功能都依赖于 Python 的异步编程模型,特别是事件循环。

二者一起使用

asyncio 和 aiofiles 在 Python 异步编程中扮演着不同的角色,但通常一起使用以提高 I/O 密集型应用程序的性能。下面我将通过一个具体的 Python 程序示例来说明它们的作用以及如何在异步编程中使用它们。

在这个示例中,我们将编写一个异步程序,该程序将同时执行以下两个任务:

  1. 使用 aiofiles 异步读取文件内容。
  2. 使用 asyncio 的 sleep 函数模拟一个耗时的计算任务。
import asyncio  
import aiofiles  
  
# 异步读取文件的函数  
async def read_file(filepath):  
    async with aiofiles.open(filepath, mode='r') as f:  
        return await f.read()  
  
# 模拟耗时计算的函数  
async def compute(x):  
    print(f"Starting computation with {x}")  
    await asyncio.sleep(2)  # 模拟耗时操作  
    return x * x  
  
# 主函数  
async def main():  
    # 并发执行读取文件和计算任务  
    read_task = asyncio.create_task(read_file('example.txt'))  
    compute_task = asyncio.create_task(compute(10))  
  
    # 等待两个任务完成  
    file_contents = await read_task  
    result = await compute_task  
  
    # 打印结果  
    print("File contents:", file_contents)  
    print("Computation result:", result)  
  
# 运行异步主函数  
asyncio.run(main())

在这个示例中,read_file 函数使用 aiofiles 异步读取文件内容,而 compute 函数则使用 asyncio.sleep 来模拟一个耗时的计算过程。main 函数中,我们同时启动了这两个任务,并通过 await 表达式等待它们完成。由于这两个任务是并发执行的,因此 compute 函数中的耗时操作不会阻塞 read_file 函数的执行,反之亦然。

请注意,为了运行此示例,你需要有一个名为 example.txt 的文件位于你的工作目录中,或者你可以将文件路径更改为指向任何存在的文件。

这个示例展示了 asyncio 和 aiofiles 如何一起工作来编写高效的异步 I/O 密集型应用程序。通过并发执行多个任务,我们可以显著提高应用程序的响应性和吞吐量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值