7.asyncio库详解

本文详细介绍了Python的asyncio库,包括其核心概念、协程和任务、事件循环的使用、异步I/O操作、定时器和调度,以及Future和Task的区别。通过实例展示了如何利用asyncio进行并发编程和提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

深入理解 Python 的 asyncio 库

Python 的 asyncio 库是一个强大的异步 I/O 框架,用于处理并发和异步编程。它提供了一种基于协程的方式来处理异步任务,使得编写异步代码更加简单和直观。

1. 什么是 asyncio?

asyncio 是 Python 3.4 引入的标准库,用于编写协程和异步代码。它基于事件循环(Event Loop)的概念,通过异步任务(coroutines)和 Future 对象来实现非阻塞的并发操作。异步编程的目标是提高代码执行效率,特别是在需要等待 I/O 操作(如网络请求、文件读写等)时,能够释放 CPU 资源,从而提高程序的并发性能。

2. 协程和任务

asyncio 中,协程是异步编程的基本单元。通过 async def 关键字定义的函数可以被视为协程。协程是可以暂停和恢复执行的函数,使得程序能够在等待 I/O 操作的同时执行其他任务。

任务(Task)是协程的一种封装,它可以在事件循环中并发执行。使用 asyncio.create_task() 函数可以创建任务,然后将任务添加到事件循环中。

3. 事件循环

事件循环是 asyncio 的核心。它负责调度和执行协程,处理事件和 I/O 操作。通过 asyncio.get_event_loop() 获取默认事件循环,然后使用 loop.run_until_complete() 来运行任务直到完成。

  • 创建事件循环

通过 asyncio.get_event_loop() 获取当前线程的事件循环,或者使用 asyncio.new_event_loop() 创建一个新的事件循环。一般情况下,我们使用 asyncio.run() 来运行主协程,该函数会自动创建和管理事件循环。

import asyncio

async def main():
    # 主协程逻辑

# 运行主协程
asyncio.run(main())
  • 启动事件循环

使用 loop.run_until_complete(coroutine) 启动事件循环并运行指定的协程,直到协程完成。

import asyncio

async def main():
    # 协程逻辑

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
  • 关闭事件循环

使用 loop.close() 关闭事件循环。在大多数情况下,不需要手动关闭事件循环,因为 asyncio.run() 在协程完成后会自动关闭事件循环。

import asyncio

async def main():
    # 协程逻辑

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
  • 运行多个任务

使用 asyncio.create_task(coroutine) 创建任务,然后使用 await asyncio.gather(task1, task2, ...) 等待多个任务同时完成。

import asyncio

async def task1():
    # 任务1逻辑

async def task2():
    # 任务2逻辑

async def main():
    t1 = asyncio.create_task(task1())
    t2 = asyncio.create_task(task2())
    await asyncio.gather(t1, t2)
  • 异步调度

事件循环通过调度器(Scheduler)来实现异步调度。协程中的 await 表达式用于暂停协程的执行,将控制权还给事件循环。当等待的条件满足时,事件循环会重新调度协程的执行。

import asyncio

async def my_coroutine():
    print("Start Coroutine")
    await asyncio.sleep(2)  # 异步等待
    print("End Coroutine")

asyncio.run(my_coroutine())
  • 定时器

使用 asyncio.sleep(seconds) 来创建一个定时器,让协程在指定时间后恢复执行。

import asyncio

async def my_coroutine():
    print("Start Coroutine")
    await asyncio.sleep(2)  # 等待2秒
    print("End Coroutine")

asyncio.run(my_coroutine())

4. 异步 I/O 操作

asyncio 通过使用 async/await 语法来处理异步 I/O 操作。例如,可以使用 asyncio.sleep() 来模拟一个异步的等待操作。

import asyncio

async def example_coroutine():
    print("Start")
    await asyncio.sleep(2)
    print("End")

asyncio.run(example_coroutine())

在上面的例子中,asyncio.sleep(2) 模拟了一个等待 2 秒的异步操作,而程序在等待期间不会被阻塞。

使用asyncio来发送http请求:

import asyncio
import time

import requests

url = 'https://www.baidu.com'

async def fetch_url(url):
    loop = asyncio.get_event_loop()
    response = await loop.run_in_executor(None, requests.get, url)
    return response.text


async def main():
    await asyncio.gather(*(fetch_url(url) for _ in range(10)))


# 运行主协程
print(time.time())
asyncio.run(main())
print(time.time())

print("------------------")

# 相比异步的方式,同步发送10次请求需要更长的时间
print(time.time())
for _ in range(10):
    requests.get(url)
print(time.time())

5. wait & gather

asyncio.wait()asyncio.gather() 都是 asyncio 中用于等待多个协程完成的方法,但它们之间有一些重要的区别。

asyncio.wait()

  1. 等待条件:

    • asyncio.wait() 用于等待多个协程中的任意一个或全部完成。可以通过设置 return_when 参数来指定等待的条件,可选值包括:
      • asyncio.FIRST_COMPLETED:等待任意一个协程完成。
      • asyncio.FIRST_EXCEPTION:等待第一个协程抛出异常。
      • asyncio.ALL_COMPLETED:等待所有协程完成。
  2. 返回值:

    • asyncio.wait() 返回一个元组,包含两个集合:
      • 第一个集合包含已完成的任务(Task)。
      • 第二个集合包含未完成的任务。
  3. 用法示例:

    done, pending = await asyncio.wait([coro1(), coro2(), coro3()], return_when=asyncio.ALL_COMPLETED)
    

asyncio.gather()

  1. 等待条件:

    • asyncio.gather() 用于同时运行多个协程,并等待它们全部完成。
  2. 返回值:

    • asyncio.gather() 返回一个包含所有协程结果的列表。
  3. 用法示例:

    results = await asyncio.gather(coro1(), coro2(), coro3())
    

总结区别:

  • 返回值类型:

    • asyncio.wait() 返回一个包含已完成和未完成任务的元组。
    • asyncio.gather() 返回一个包含所有协程结果的列表。
  • 等待条件:

    • asyncio.wait() 允许你设置等待条件,可以等待任意一个或全部协程完成,或者等待第一个异常。
    • asyncio.gather() 等待所有协程同时完成。
  • 适用场景:

    • 如果你需要对每个协程的完成状态进行额外的处理,或者需要根据条件选择部分协程执行,可以使用 asyncio.wait()
    • 如果你希望并发运行多个协程,并在所有协程都完成后获取它们的结果,可以使用 asyncio.gather()

下面是一个简单的示例,演示了两者的用法:

import asyncio


async def coro1():
    await asyncio.sleep(1)
    print("Task 1 completed")
    return 1


async def coro2():
    await asyncio.sleep(2)
    print("Task 2 completed")
    return 2


async def coro3():
    await asyncio.sleep(0.5)
    print("Task 3 completed")
    return 3


async def task1():
    pass


async def main():
    # 使用 asyncio.wait()
    task1 = asyncio.create_task(coro1())
    task2 = asyncio.create_task(coro2())
    task3 = asyncio.create_task(coro3())
    done, pending = await asyncio.wait([task1, task2, task3], return_when=asyncio.ALL_COMPLETED)
    print(f"Done tasks: {[task.result() for task in done]}")
    print(f"Pending tasks: {[task for task in pending]}")

    # 使用 asyncio.gather()
    results = await asyncio.gather(coro1(), coro2(), coro3())
    print(f"Gathered results: {results}")


# 运行主协程
asyncio.run(main())

这个例子中,asyncio.wait() 返回一个已完成的任务集合和一个未完成的任务集合,而 asyncio.gather() 直接返回所有协程的结果列表。

6. Task & Future

asyncio 中,TaskFuture 都是用于处理异步操作的类。它们之间有一些区别,但也有相似之处。

Future

asyncio.Future 是一个表示异步操作结果的对象。它是一个容器,用于存储异步操作的最终结果或异常。Future 对象通常由 asyncio 方法返回,表示一个尚未完成的异步操作。

用法
  1. 手动创建 Future 对象:

    import asyncio
    
    async def my_coroutine():
        fut = asyncio.Future()
        fut.set_result("Hello, Future!")
        return fut
    
    async def main():
        result = await my_coroutine()
        print(result.result())
    
    asyncio.run(main())
    
  2. 通过 asyncio.ensure_future() 包装协程:

    import asyncio
    
    async def my_coroutine():
        return "Hello, Future!"
    
    async def main():
        fut = asyncio.ensure_future(my_coroutine())
        print(await fut)
    
    asyncio.run(main())
    

Task

asyncio.Task 是对 Future 的高级封装,它表示一个异步操作的执行单元。通常情况下,我们不直接创建 Task 对象,而是通过 asyncio.create_task()asyncio.ensure_future() 来创建。asyncio.Taskasyncio.Future 的子类,它表示一个异步操作的执行单元,通常对应一个协程。Task 提供了一些额外的方法和属性,用于管理和控制任务的执行。

创建Task
  1. 使用 asyncio.create_task() 创建任务:

    import asyncio
    
    async def my_coroutine():
        return "Hello, Task!"
    
    async def main():
        task = asyncio.create_task(my_coroutine())
        print(await task)
    
    asyncio.run(main())
    
  2. 使用 asyncio.ensure_future() 包装协程创建任务:

    import asyncio
    
    async def my_coroutine():
        return "Hello, Task!"
    
    async def main():
        task = asyncio.ensure_future(my_coroutine())
        print(await task)
    
    asyncio.run(main())
    
Task常见方法
  1. asyncio.Task.get_loop()
  • 描述:返回与任务关联的事件循环

  • 用法示例:

    import asyncio
    
    async def my_coroutine():
        print("Task loop:", asyncio.Task.get_loop())
    
    async def main():
        task = asyncio.create_task(my_coroutine())
        await task
    
    asyncio.run(main())
    
  1. asyncio.Task.cancel()
  • 描述:取消任务,将任务的状态设置为已取消

  • 用法示例:

    import asyncio
    
    async def my_coroutine():
        try:
            await asyncio.sleep(5)
        except asyncio.CancelledError:
            print("Task was cancelled")
    
    async def main():
        task = asyncio.create_task(my_coroutine())
        await asyncio.sleep(2)
        task.cancel()
        await asyncio.sleep(1)  # 等待任务响应取消
    
    asyncio.run(main())
    
  1. asyncio.Task.done()
  • 描述:返回一个 TrueFalse,表示任务是否已完成

  • 用法示例:

    import asyncio
    
    async def my_coroutine():
        print("Task is done:", asyncio.current_task().done())
    
    async def main():
        task = asyncio.create_task(my_coroutine())
        await task
    
    asyncio.run(main())
    
  1. asyncio.Task.exception()
  • 描述:返回任务的异常,如果任务没有异常则返回 None

  • 用法示例:

    import asyncio
    
    async def my_coroutine():
        raise ValueError("Custom error")
    
    async def main():
        task = asyncio.create_task(my_coroutine())
        try:
            await task
        except Exception as e:
            print("Task exception:", task.exception())
    
    asyncio.run(main())
    
  1. asyncio.Task.result()
  • 描述:返回任务的结果。如果任务还没有完成,则引发 asyncio.InvalidStateError 异常

  • 用法示例:

    import asyncio
    
    async def my_coroutine():
        return "Task result"
    
    async def main():
        task = asyncio.create_task(my_coroutine())
        await task
        print("Task result:", task.result())
    
    asyncio.run(main())
    
  1. asyncio.Task.add_done_callback(callback, *args)
  • 描述:添加一个回调函数,在任务完成时调用

  • 用法示例:

    import asyncio
    
    def callback(task, *args):
        print("Callback executed")
    
    async def my_coroutine():
        print("Task completed")
    
    async def main():
        task = asyncio.create_task(my_coroutine())
        task.add_done_callback(callback)
        await task
    
    asyncio.run(main())
    

Future 和 Task 区别:

  1. 创建方式:

    • Future 对象可以手动创建,也可以由 asyncio.ensure_future()asyncio.Future() 创建。
    • Task 对象通常由 asyncio.create_task()asyncio.ensure_future() 创建。
  2. 使用场景:

    • Future 主要用于表示异步操作的结果或异常,可以手动设置结果。
    • Task 是对 Future 的高级封装,用于表示协程的执行单元,通常在创建时就已经与某个协程绑定。
  3. 状态检查:

    • 通过 fut.done() 可以检查 Future 是否已经完成。
    • Task 对象有额外的状态检查方法,如 task.cancelled()task.running()

在实际应用中,Task 对象更常用,因为它是对 Future 的进一步封装,提供了更多的功能,例如取消任务、绑定回调函数等。但在某些情况下,可能仍然需要直接使用 Future 对象,例如手动控制异步操作的完成状态。

asyncio库总结

asyncio 库提供了多个方法和类,用于编写异步代码。以下是一些常用的 asyncio 方法和类的概览:

主要方法和类:

  1. asyncio.run(coroutine, *, debug=False)

    • 用于运行一个协程,启动事件循环,然后在协程执行完成后关闭事件循环。
  2. asyncio.create_task(coroutine, *, name=None)

    • 用于创建一个任务,将协程添加到事件循环中,并返回任务对象。
  3. asyncio.gather(*coros, loop=None, return_exceptions=False)

    • 用于同时运行多个协程,并等待它们全部完成。返回一个包含所有协程结果的列表。
  4. asyncio.wait(coros, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

    • 用于等待多个协程中的一个或全部完成。返回一个包含已完成任务的集合。
  5. asyncio.sleep(delay, result=None, *, loop=None)

    • 用于在协程中进行异步休眠,推迟执行。

事件循环控制方法:

  1. loop.run_until_complete(future)

    • 用于运行事件循环直到指定的 Future 完成。通常用于运行主协程。
  2. loop.run_forever()

    • 用于启动事件循环并持续运行,直到调用 loop.stop()
  3. loop.stop()

    • 用于停止事件循环。
  4. loop.close()

    • 用于关闭事件循环。

其他工具方法:

  1. asyncio.shield(what, *, loop=None)

    • 用于创建一个被保护的 Future 对象,防止被取消。
  2. asyncio.ensure_future(coro_or_future, *, loop=None)

    • 用于将协程或 Future 对象包装成 Future 对象。
  3. asyncio.current_task(loop=None)

    • 用于获取当前任务(Task)对象。
  4. asyncio.all_tasks(loop=None)

    • 用于获取事件循环中的所有任务。
  5. asyncio.run_coroutine_threadsafe(coro, loop)

    • 用于在不同线程中运行协程。

信号处理:

  1. loop.add_signal_handler(signal, callback, *args)
    • 用于添加信号处理器。

定时器和调度:

  1. loop.call_later(delay, callback, *args, context=None)

    • 用于在指定延迟后调用指定回调函数。
  2. loop.call_at(when, callback, *args, context=None)

    • 用于在指定时间后调用指定回调函数。

异步 I/O 操作:

  1. loop.add_reader(fd, callback, *args)loop.remove_reader(fd)

    • 用于监听文件描述符上的可读事件。
  2. loop.add_writer(fd, callback, *args)loop.remove_writer(fd)

    • 用于监听文件描述符上的可写事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值