在 Python 异步编程中使用 `awaitable` 对象

在 Python 异步编程中使用 awaitable 对象

awaitable 对象上,我们可以应用 await 语句。asyncio 中的大部分函数和模块被设计为可以与 awaitable 对象一起使用。但是大部分 Python 对象和第三方库,不是为异步编程构建的。当构建异步应用程序时,有一点非常重要,那就是选择提供可用 awaitble 对象的兼容库。

awaitalbe 对象主要被分成三种类型:协程、任务和期物 (Futures)。我们已经讨论过了协程和任务。期物是一种低层次对象,像一种回调机制,用来处理来自于async/await的结果。期物对象通常不会暴露于用户级编程。

并发运行任务

如果我们一定要并行运行多个任务,我们可以使用 await 关键字,就像我们在之前的例子中做的那样。但是有一种更好的方法做这件事,就是使用 gather 函数。这个函数会按提供的序列运行 awaitable 对象。如果任何awaitable 对象是协程,它将被调度为任务。后面的例子中我们会演示 gather 函数的使用。

使用队列分发任务

asyncio 包中的 Queue 队列和 Queue 模块类似,但是不是线程安全的。但是 asyncio 模块提供了多种队列实现,比如 FIFO 队列、优先级队列和LIFO队列。asyncio 模块中的队列可以用来把工作负载发布为任务。

为了演示任务对列的使用,我们会写一个小程序,通过随机休眠一段时间来模拟实际函数的执行时间。为 10 次这样的执行计算随机休眠时间,并且由主进程作为工作项添加到 Queue 对象中。Queue 队列被传递到一个有三个任务的池中。任务池中的每一个任务执行给定的协程,协程会消费每一个对它可见的队列记录中的执行时间(该协程根据其可用的队列条目消耗执行时间)。下面是完整的代码示例:

import time

async def executer(name, queue):
    while True:
        exec_time = await queue.get()
        await asyncio.sleep(exec_time)
        queue.task_done()

async def main():
    myqueue = asyncio.Queue()
    calc_execution_time = 0
    for _ in range(10):
        sleep_for = random.uniform(0.4, 0.8)
        calc_execution_time += sleep_for
        myqueue.put_nowait(sleep_for)

    tasks = []
    for id in range(3):
        task = asyncio.create_task(executer(f'任务-{id + 1}', myqueue))
        tasks.append(task)

    start_time = time.monotonic()
    await myqueue.join()
    total_exec_time = time.monotonic() - start_time

    for task in tasks:
        task.cancel()

    await asyncio.gather(*tasks, return_exceptions=True)

    print(f"执行时间:{calc_execution_time:0.2f}")
    print(f"实际执行时间:{total_exec_time:0.2f}")


asyncio.run(main())

我们使用 Queue 队列的 put_nowait 函数,因为它是一个非阻塞操作。上面的代码的执行结果是:

执行时间:6.22
实际执行时间:2.40

这清晰的演示了任务是并行执行的,执行时间是顺序执行的近三分之一。

<完>

<think>好的,我现在需要向用户介绍Python异步编程。首先,我要确保自己理解异步编程的基本概念,然后逐步分解,用清晰的结构解释给用户。 用户可能已经知道同步编程是怎样的,所以可以从同步和异步的区别入手。比如,同步是按顺序执行,遇到阻塞操作(如I/O)时会等待,而异步可以在等待时执行其他任务,提高效率。 接下来需要解释异步编程的核心概念,比如事件循环、协程、await/async关键字。事件循环是异步的基础,负责调度任务。协程是用async def定义的函数,可以被挂起和恢复。await用来调用可等待对象,让出控制权。 然后要说明异步编程的优势,比如高并发、资源高效,适合I/O密集型任务。但也要提到缺点,比如不适合CPU密集型任务,代码复杂度较高,需要特定库的支持。 还需要给出一个简单的代码示例,展示async和await的用法,让用户有直观的认识。比如使用asyncio库创建一个异步函数,执行多个任务。 最后,可以提到常用的异步库,如aiohttp、asyncpg,以及异步框架FastAPI,帮助用户了解实际应用场景。 在整理这些点时,要确保使用正确的中文术语,避免技术术语的混淆。同时,按照用户的要求,数学公式用$...$格式,但这里可能用不上。结构要清晰,分点说明,逐步引导用户理解异步编程的各个方面。 需要注意检查是否有错误的信息,比如事件循环的作用是否正确,协程的定义是否准确。确保示例代码正确无误,能够运行。同时,提醒用户异步编程的适用场景,避免误用。 可能用户是开发者,想了解如何提升程序性能,特别是在处理网络请求或文件I/O时。所以需要强调异步在这些场景下的优势,以及如何正确使用异步编程来避免常见的问题,比如阻塞事件循环。 总结下来,回答的结构应该是:定义异步编程,对比同步模型,核心概念,优势与局限,示例代码,常用库和应用场景。这样用户能有一个全面的认识,并知道下一步如何开始使用异步编程。</think>Python异步编程是一种通过非阻塞方式处理高并发 I/O 操作的编程范式。以下是逐步解析: --- ### 一、同步 vs 异步 - **同步模型**:代码按顺序执行,遇到 I/O 操作(如网络请求、文件读写)时会**阻塞等待**,导致资源闲置。 ```python # 同步示例:每个请求依次等待 import requests for url in urls: response = requests.get(url) # 阻塞直到返回 ``` - **异步模型**:遇到 I/O 操作时**暂停当前任务**,切换到其他任务,充分利用 CPU 资源。 ```python # 异步示例:多个请求并发执行 import aiohttp async def fetch(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: # 遇到 I/O 时切换任务 return await response.text() ``` --- ### 二、核心概念 1. **事件循环(Event Loop)** 负责调度协程的执行,监测 I/O 事件。类比“交通指挥中心”。 ```python import asyncio loop = asyncio.get_event_loop() # 获取事件循环 ``` 2. **协程(Coroutine)** 通过 `async def` 定义的函数,可被挂起和恢复。使用 `await` 显式声明挂起点。 ```python async def my_task(): print("Start") await asyncio.sleep(1) # 挂起协程,1秒后恢复 print("End") ``` 3. **可等待对象Awaitable)** 包括协程、Task 和 Future。`await` 关键字只能用于这些对象。 --- ### 三、代码结构 典型异步程序包含: 1. 定义协程函数(`async def`) 2. 创建任务(`asyncio.create_task()`) 3. 运行事件循环(`asyncio.run()`) **示例**: ```python import asyncio async def task1(): await asyncio.sleep(2) print("Task 1 done") async def task2(): await asyncio.sleep(1) print("Task 2 done") async def main(): t1 = asyncio.create_task(task1()) t2 = asyncio.create_task(task2()) await t1 await t2 asyncio.run(main()) # 总耗时约2秒(非顺序执行) ``` --- ### 四、适用场景 | 场景类型 | 说明 | |----------------|--------------------------| | **I/O 密集型** | 网络请求、数据库操作等 | | **高并发** | Web 服务器、爬虫等 | | **不适合** | CPU 密集型计算(需用多进程) | --- ### 五、优缺点 - **优势** - 资源利用率高(单线程处理数千并发) - 延迟更低(无线程切换开销) - **局限** - 代码复杂度增加(需避免阻塞操作) - 需要特定库支持(如 `aiohttp` 替代 `requests`) --- ### 六、常用工具 1. 异步框架:`FastAPI`、`Sanic` 2. 数据库库:`asyncpg`、`aiomysql` 3. 网络请求:`aiohttp` 4. 任务调度:`asyncio.gather()` --- ### 七、典型问题 1. **阻塞事件循环** 错误示例: ```python async def bad_task(): time.sleep(5) # 同步阻塞函数,破坏异步! ``` 正确写法: ```python async def good_task(): await asyncio.sleep(5) # 异步非阻塞 ``` 2. **任务协调** 使用 `asyncio.gather()` 管理多个任务: ```python async def main(): await asyncio.gather(task1(), task2(), task3()) ``` --- 通过理解这些核心机制,您可以更高效地开发高并发 Python 应用。建议从简单的 `asyncio` 示例开始实践,逐步掌握异步编程模式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dowhileprogramming

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值