【Python 异步编程学习手册】

Python 异步编程学习手册

本手册面向初学者和进阶者,介绍 Python 标准库中的异步编程相关知识点。内容涵盖同步与异步的区别、async/await 语法、协程、事件循环、任务与并发、Future 对象、控制结构、异步 I/O、异步上下文管理器与迭代器、异常与任务取消、并发原语(锁、信号量、队列等)、常用工具函数(asyncio.runasyncio.sleepasyncio.to_threadloop.run_in_executor)、线程/进程池交互,以及性能注意事项。每节均附有说明和示例代码,帮助读者深入理解。

同步 vs 异步 简介

  • 同步 (Synchronous):一个操作 A 必须等待前一个操作 B 完成后才能开始,也就是按顺序执行。例如:

    python 复制代码
    import time
    
    def task_A():
        print("Task A 开始")
        time.sleep(2)   # 模拟耗时操作(阻塞)
        print("Task A 完成")
    
    def task_B():
        print("Task B 开始")
        time.sleep(1) 
        print("Task B 完成")
    
    task_A()  # 执行完后才开始 task_B
    task_B()

    在同步模型中,task_B 只有在 task_A 完全结束后才开始执行[0]。整个程序执行过程中若遇到阻塞(如 time.sleep、I/O 操作等),就会等待完成后才继续。

  • 异步 (Asynchronous):操作 A 不需要等待操作 B 完成,可以同时进行,或者在等待 B 的过程中执行其他任务。例如:

    python 复制代码
    import asyncio
    
    async def task_A():
        print("Task A 开始")
        await asyncio.sleep(2)   # 异步等待,不阻塞事件循环
        print("Task A 完成")
    
    async def task_B():
        print("Task B 开始")
        await asyncio.sleep(1)
        print("Task B 完成")
    
    async def main():
        # 同时启动两个协程,让它们异步运行
        await asyncio.gather(task_A(), task_B())
    
    asyncio.run(main())

    在上述异步示例中,task_Atask_B 可以并发运行,互不等待[1]。事件循环负责在它们执行 await 时切换,从而提高效率。异步编程强调 非阻塞调用 :在发出调用后,调用方不会一直等待,而是可以继续执行其他代码;而当结果可用时,通过回调、事件或 await 机制得到通知[2][3]

async/await 基础语法

  • 使用 async def 定义异步函数(协程函数),在函数内部可以使用 await 来暂停等待一个可等待对象(awaitable)。async defawait 是 Python 标准库 asyncio 的核心关键字[4]

  • 协程定义与调用async def 声明函数为协程函数。调用协程函数时会返回一个 协程对象 ,但不会立即执行,必须通过 await 或在事件循环中调度才能运行[5]。例如:

    python 复制代码
    import asyncio
    
    async def hello():
        print("Hello")
        await asyncio.sleep(1)
        print("World")
    
    # 单个协程执行示例
    asyncio.run(hello())

    运行该代码,输出将是 Hello、然后等待 1 秒再输出 World[4]。注意:直接调用 hello() 不会执行协程,只返回一个协程对象;只有通过 asyncio.runawait hello() 才真正执行。

  • await 表达式await 用于挂起当前协程并等待可等待对象完成,通常是另一个协程、asyncio.Futureasyncio.Task[6]。例如:

    python 复制代码
    async def say_after(delay, msg):
        await asyncio.sleep(delay)
        print(msg)
    
    async def main():
        print("开始等待")
        await say_after(1, "异步消息")
        print("完成等待")
    
    asyncio.run(main())

    main 中,await say_after(1, ...) 会先打印"开始等待",然后等待 1 秒后打印"异步消息",最后再打印"完成等待"[7]

  • 重要注意事项 :只能在 async def 定义的协程内部使用 await;在常规函数或全局作用域使用 await 会导致 SyntaxError[8]。同时,如果协程函数内部不含有任何 awaitreturnyield,它仍然是一个协程函数,但通常会立即执行完毕。在异步函数里,可以像同步函数一样抛出异常或返回值,调用者可以通过 await 捕获异常或获得返回值。

协程(Coroutines)

  • 概念 :协程是 Python 中对异步函数的调用结果,是一种可等待(awaitable)的对象。当使用 async def 定义的函数被调用时,返回的是协程对象[9]。协程对象必须被 await 或调度到事件循环中才能执行。

  • 协程函数 vs 协程对象:Python 文档将"协程"一词用于两种意义:

    • 协程函数:使用 async def 定义的函数本身。
    • 协程对象:调用协程函数后返回的对象。
      例如:
    python 复制代码
    async def func():
        return 42
    
    coro = func()  # 此时 func() 返回一个协程对象,但尚未执行

    只有对 coro 进行 await 或通过事件循环调度时,函数体才会运行并返回值[9]

  • 示例

    python 复制代码
    import asyncio
    
    async def nested():
        return 42
    
    async def main():
        # 如果只是调用 nested() 而不 await,它不会运行:
        # nested()  # 不执行任何操作,只产生一个协程对象[[10]](https://docs.python.org/3/library/asyncio-task.html#:~:text=,RuntimeWarning)
    
        # 正确的做法是使用 await:
        result = await nested()
        print(result)  # 输出 42
    
    asyncio.run(main())

    在上例中,await nested() 使 nested 协程真正运行并返回结果 42[10]

事件循环(Event Loop)

  • 作用 :事件循环是异步应用的核心,负责调度和执行所有协程、任务、回调,以及处理网络 I/O 等操作[11]。在一个进程中通常只有一个事件循环(单线程模式),由它来切换运行各个协程。Python 应用开发者一般无需直接操作事件循环,而使用高级函数(如 asyncio.run())启动协程,但了解其概念有助于理解运行机制[11][12]

  • 常用操作 :可以通过 asyncio.get_running_loop() 在协程内部获取当前正在运行的事件循环[13],也可以使用 asyncio.new_event_loop() 创建新的循环、asyncio.set_event_loop() 设置当前循环。通常我们使用 asyncio.run(main()) 自动管理事件循环的启动与关闭[7]

  • 示例(手动使用事件循环;高级用法可略):

    python 复制代码
    import asyncio
    
    async def say_hi():
        await asyncio.sleep(1)
        print("Hi from coroutine")
    
    # 手动创建并运行事件循环(通常用 asyncio.run 更简单)
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        loop.run_until_complete(say_hi())
    finally:
        loop.close()

    这里 run_until_complete 会运行循环直到协程结束。一般情况下,推荐使用 asyncio.run 启动顶层协程,而不直接操作循环[7]

任务(Tasks)与并发

  • 任务(Task)asyncio.Task 是对协程的封装,用于将协程调度为独立执行的单元。调用 asyncio.create_task(coro) 会立即安排协程 coro 在事件循环中尽快执行,并返回一个 Task 对象[14]Task 继承自 Future,因此可以 await 等待其完成。示例:

    python 复制代码
    import asyncio
    
    async def say(msg, delay):
        await asyncio.sleep(delay)
        print(msg)
    
    async def main():
        # 同时创建两个任务并发执行
        task1 = asyncio.create_task(say("Hello", 1))
        task2 = asyncio.create_task(say("World", 2))
        print("Tasks 已创建")
        await task1
        await task2
        print("Tasks 完成")
    
    asyncio.run(main())

    输出类似:

    复制代码
    Tasks 已创建
    Hello   # 大约1秒后
    World   # 大约2秒后
    Tasks 完成

    两个任务并发执行,使总耗时约为最长的延迟 2 秒[15][16]

  • 并发机制 :事件循环采用协作式调度 ,一次只运行一个任务。当一个 Task 在执行中遇到 await 时,将控制权让回给事件循环,循环可以调度其他任务执行[17]。这与多线程不同:虽然协程看似并发运行,但实际仍在一个线程内顺序执行,只有在遇到 I/O 等待时才切换上下文[18][17]

  • 注意事项 :如果创建了 Task 却不保存引用,也不 await,那么该任务可能在执行过程中被垃圾回收,无法完成。官方文档建议将需要"后台运行"的任务保存在集合中,并在完成后移除引用,以避免任务在执行前被回收[19]。例如:

    python 复制代码
    import asyncio
    
    async def worker(n):
        await asyncio.sleep(n)
        print(f"worker {n} done")
    
    async def main():
        background = set()
        for i in range(3):
            task = asyncio.create_task(worker(i))
            background.add(task)
            task.add_done_callback(background.discard)
        await asyncio.sleep(5)  # 等待足够时间让所有后台任务完成
    
    asyncio.run(main())

    在上例中,background 集合保持了对任务的引用,防止它们在完成前被 GC 回收[19]

Future 对象

  • 概念asyncio.Future 是一个底层的可等待对象,用于表示异步操作的最终结果。Future 相当于预留一个位置,等待异步操作在未来某个时间"完成"并填入结果。协程可以 await 一个 Future,当 Future 被设置结果或异常时,协程恢复运行[20]。通常应用代码不需要直接创建 Future,而是由事件循环或第三方库返回。例如,loop.run_in_executor() 就返回一个 Future,供协程等待其结果[20][21]

  • 示例 :下面演示手动创建 Future 并在事件循环中完成它(注意,这只是为了示例,实际很少手动做):

    python 复制代码
    import asyncio
    
    async def main():
        loop = asyncio.get_running_loop()
        # 创建一个 Future
        fut = loop.create_future()
        # 在 1 秒后为 Future 设置结果
        loop.call_later(1, fut.set_result, "完成")
        # 等待 Future 完成
        result = await fut
        print("Future 返回:", result)
    
    asyncio.run(main())

    这里 fut 在 1 秒后被设置了结果 "完成",此时 await fut 结束并返回该结果[20]

asyncio.gatherasyncio.waitasyncio.shield 等控制结构

  • asyncio.gather :用于并发运行多个 awaitable(协程、任务或 Future),并将它们的结果收集到列表中。默认情况下,gather 会在任一 awaitable 抛出异常时将异常向上抛出,而其他 awaitable 继续运行[22]。示例:

    python 复制代码
    import asyncio
    
    async def work(n):
        await asyncio.sleep(n)
        return n
    
    async def main():
        results = await asyncio.gather(work(1), work(2), work(3))
        print(results)  # [1, 2, 3]
    
    asyncio.run(main())

    上例中,三个 work 协程并发执行,最终结果为 [1, 2, 3]

  • asyncio.wait :类似于 gather,可以并发等待多个任务,但是返回两个集合:完成的 (done) 和未完成的 (pending)[23]。常用 return_when 参数控制行为,如 asyncio.FIRST_COMPLETED 表示只要有一个任务完成就返回;asyncio.ALL_COMPLETED(默认)表示等待所有完成;asyncio.FIRST_EXCEPTION 表示任何任务出现异常时就返回[24][25]。与 gather 不同,wait 不会取消尚未完成的任务,仅返回状态。示例:

    python 复制代码
    done, pending = await asyncio.wait(
        [asyncio.create_task(work(1)), asyncio.create_task(work(2))],
        return_when=asyncio.FIRST_COMPLETED
    )
    print("完成的任务数:", len(done))
  • asyncio.shield :用于屏蔽取消 。如果对一个协程使用 shield,那么即使包含它的父协程被取消,该协程内部运行也不会被自动取消[26]。例如:

    python 复制代码
    import asyncio
    
    async def inner():
        try:
            await asyncio.sleep(5)
            return "inner 完成"
        except asyncio.CancelledError:
            print("inner 收到取消")
            raise
    
    async def main():
        task = asyncio.create_task(inner())
        try:
            # 即使 main 被取消,inner 仍会继续运行完成
            result = await asyncio.shield(task)
        except asyncio.CancelledError:
            print("main 收到取消")
        else:
            print("任务返回:", result)
    
    # 在 1 秒后取消 main 任务
    async def runner():
        main_task = asyncio.create_task(main())
        await asyncio.sleep(1)
        main_task.cancel()
        try:
            await main_task
        except:
            pass
    
    asyncio.run(runner())

    在上述代码中,await asyncio.shield(task) 保证了即使外层 main() 被取消,inner() 也会继续运行直到完成[26]。如果希望完全忽略取消,可以在 shield 外层捕获 CancelledError[27]

  • 超时等待asyncio.wait_for(aw, timeout) 等待指定的 awaitable 完成,超时则取消其任务并抛出 asyncio.TimeoutError[28]。示例:

    python 复制代码
    async def slow():
        await asyncio.sleep(10)
    
    async def main():
        try:
            await asyncio.wait_for(slow(), timeout=2)
        except asyncio.TimeoutError:
            print("操作超时!")
    
    asyncio.run(main())

    如上,如果 slow() 运行超过 2 秒,则抛出超时异常。

异步 I/O 与 asyncio.StreamReader/StreamWriter

  • 异步 I/Oasyncio 提供了高层次的网络流(stream)接口,封装了 socket 读写,使编程更简洁。按照官方文档,Streams 是"高层次的 async/await 原语,用于处理网络连接,允许发送和接收数据而无需使用回调或底层协议 "[29]

  • 常用函数asyncio.open_connection(host, port) 建立 TCP 连接,返回 (StreamReader, StreamWriter) 对象对[30]。类似地,asyncio.start_server 可启动一个服务器,并在新连接时回调给定的协程,提供 (reader, writer)StreamReader 可以用于读取数据,StreamWriter 用于写入数据。

  • 示例(TCP 回声客户端):

    python 复制代码
    import asyncio
    
    async def tcp_echo_client(msg):
        reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
        print(f"发送:{msg!r}")
        writer.write(msg.encode())
        await writer.drain()  # 确保数据发送
        data = await reader.read(100)
        print(f"接收:{data.decode()!r}")
        writer.close()
        await writer.wait_closed()
        print("连接已关闭")
    
    asyncio.run(tcp_echo_client("Hello, AsyncIO!"))

    在该示例中,客户端通过 open_connection 获取读写流,发送数据后用 await writer.drain() 等待缓冲区刷新,然后用 await reader.read() 读取服务器响应,最后关闭连接[31]

异步上下文管理器 与 异步迭代器

  • 异步上下文管理器 :使用 async with 语法,可以在异步上下文中自动管理资源。Python 提供了 @contextlib.asynccontextmanager 装饰器,简化异步上下文管理器的定义。其用法类似于 @contextlib.contextmanager,但用于 async with 语句[32]。例如:

    python 复制代码
    from contextlib import asynccontextmanager
    import asyncio
    
    @asynccontextmanager
    async def acquire_resource():
        print("获取资源")
        await asyncio.sleep(0.5)  # 模拟异步获取资源
        resource = "RESOURCE"
        try:
            yield resource
        finally:
            print("释放资源")
            await asyncio.sleep(0.5)  # 模拟清理
            
    async def main():
        async with acquire_resource() as r:
            print("使用资源:", r)
    
    asyncio.run(main())

    运行结果会先输出"获取资源",然后在 async with 块中输出"使用资源",最后在块退出时自动执行 finally 中的释放操作[33]

  • 异步迭代器 :使用 async for 可以异步迭代一个可迭代对象。常见的模式是定义异步生成器函数:在 async def 函数中使用 yield,并且可以包含 await 来异步生成序列[34]。例如,一个简单的异步计数生成器:

    python 复制代码
    async def async_range(start, end):
        for i in range(start, end):
            await asyncio.sleep(0.3)  # 模拟异步获取下一个值
            yield i
    
    async def main():
        async for n in async_range(1, 6):
            print(n)
    
    asyncio.run(main())

    该示例中,async_range 定义了一个异步生成器,每次 yield 一个值时异步暂停。使用 async for 可以依次取出这些值[34],打印 1 到 5。

    如果需要更复杂的状态,可以自己实现类的异步迭代器,需要定义 .__aiter__().__anext__() 方法。然而,在实践中,大多数情况下直接使用异步生成器是更便捷的方式[34]

异常处理 与 任务取消

  • 任务取消 :可以对 Task 调用 .cancel() 来取消其执行。当任务被取消时,会在协程内抛出 asyncio.CancelledError 异常[35]。协程应当使用 try/finallytry/except 捕获 CancelledError 以执行必要的清理,并在处理完后重新抛出(因为 CancelledError 属于 BaseException,正常的 except Exception 捕获不到它)[36]。例如:

    python 复制代码
    import asyncio
    
    async def worker():
        try:
            print("工作开始")
            await asyncio.sleep(5)
            print("工作完成")
        finally:
            print("收到取消,进行清理")
    
    async def main():
        task = asyncio.create_task(worker())
        await asyncio.sleep(1)
        task.cancel()
        try:
            await task
        except asyncio.CancelledError:
            print("任务已取消")
    
    asyncio.run(main())

    输出示例:

    复制代码
    工作开始
    收到取消,进行清理
    任务已取消

    在上例中,workerawait asyncio.sleep 时被取消,并进入 finally 执行清理操作[36]

  • 超时与异常 :使用 asyncio.wait_for 超时会取消任务并抛出 TimeoutError。此外,任何协程中抛出的异常都可以像同步代码一样被外层捕获。例如:

    python 复制代码
    async def faulty():
        raise ValueError("出现错误")
    
    async def main():
        try:
            await faulty()
        except ValueError as e:
            print("捕获到异常:", e)
    
    asyncio.run(main())

    可捕获协程内的异常。asyncio.wait_for 举例请参见上节。

信号量、锁、队列 等并发原语

Python 的 asyncio 提供了多种并发原语,类似于 threading 模块,但专为同一个线程内的协程设计:

  • 互斥锁 asyncio.Lock :用于在多个协程之间保证一次只有一个能访问共享资源。使用 async with lock: 比直接 await lock.acquire() 更简单[37]。示例:

    python 复制代码
    import asyncio
    
    lock = asyncio.Lock()
    
    async def worker(name):
        async with lock:
            print(f"{name} 获得锁,进入临界区")
            await asyncio.sleep(1)
            print(f"{name} 离开临界区")
    
    async def main():
        await asyncio.gather(worker("A"), worker("B"))
    
    asyncio.run(main())

    在该示例中,A、B 两个协程争夺同一个锁,所以它们不会同时进入临界区,而是串行执行[37]

  • 信号量 asyncio.Semaphore :管理一个内部计数器,用来控制同时访问某资源的最大协程数。调用 await sem.acquire() 时,如果计数器已为 0,则挂起等待;release() 会将计数器加 1[38]async with sem: 等价于 acquire/release 组合使用[37]。示例(允许最多 2 个协程同时进入临界区):

    python 复制代码
    import asyncio
    
    sem = asyncio.Semaphore(2)
    
    async def task(n):
        async with sem:
            print(f"任务{n}进入临界区")
            await asyncio.sleep(1)
            print(f"任务{n}离开临界区")
    
    async def main():
        await asyncio.gather(*(task(i) for i in range(5)))
    
    asyncio.run(main())

    运行时,Semaphore 限制同时运行的任务数不超过 2[38][37]

  • 队列 asyncio.Queue :异步队列用于在生产者和消费者协程之间传递数据,类似于 queue.Queue。队列方法如 put()get() 都是异步的,默认队列无限大(maxsize=0),也可指定最大长度[39]。示例:多个任务从队列取数据并处理,直到队列空:

    python 复制代码
    import asyncio, random, time
    
    async def worker(name, queue):
        while True:
            # 等待队列中的一项
            item = await queue.get()
            # 处理项
            print(f"{name} 处理 {item}")
            await asyncio.sleep(item)
            # 通知队列工作完成
            queue.task_done()
    
    async def main():
        queue = asyncio.Queue()
        # 将 5 个随机任务加入队列
        for i in range(5):
            delay = random.uniform(0.1, 1.0)
            queue.put_nowait(delay)
    
        # 启动三个 worker 协程
        tasks = [asyncio.create_task(worker(f"worker-{i}", queue)) for i in range(3)]
        start = time.monotonic()
        await queue.join()  # 等待队列处理完成
        elapsed = time.monotonic() - start
        print(f"总耗时:{elapsed:.2f} 秒")
    
        # 取消 worker 协程
        for t in tasks:
            t.cancel()
    
    asyncio.run(main())

    在上述例子中,多个协程并发消费同一个队列,使用 await queue.get() 获取任务,处理后调用 queue.task_done() 通知完成。使用 await queue.join() 等待所有任务处理完毕[40]

  • 其他原语asyncio.Eventasyncio.Conditionasyncio.Barrier 等提供了更复杂的同步机制,使用方式类似于线程同步原语。这些原语都是非线程安全的,仅供同一事件循环中的协程使用。具体用法可参考官方文档。

asyncio.runasyncio.sleepasyncio.to_threadrun_in_executor

  • asyncio.run :在最高层启动一个协程,自动管理事件循环的创建、运行和关闭。这是运行异步程序的推荐方式[7]。示例:asyncio.run(main())

  • asyncio.sleep :异步版本的休眠函数,不阻塞事件循环。与 time.sleep() 不同,await asyncio.sleep(n) 在 n 秒内让出控制权,允许其他协程运行[7]。示例:await asyncio.sleep(1)

  • asyncio.to_thread :Python 3.9 引入,用于在后台线程中运行阻塞函数,并返回一个可等待对象(Future)。这主要用于避免 I/O 密集型或阻塞调用阻塞事件循环[41][42]。例如将普通的文件操作或计算任务放到线程池:

    python 复制代码
    import asyncio
    import time
    
    def blocking():
        print("开始阻塞操作")
        time.sleep(1)
        print("阻塞操作完成")
    
    async def main():
        print("启动异步任务")
        await asyncio.to_thread(blocking)  # 在子线程执行 blocking()
        print("异步任务结束")
    
    asyncio.run(main())

    输出示例为:先"开始阻塞操作",1 秒后"阻塞操作完成",再输出"异步任务结束",期间事件循环没有被阻塞[43]

  • loop.run_in_executor :用于在指定的线程池或进程池中运行阻塞函数。必须通过事件循环对象调用(通常通过 asyncio.get_running_loop()),并传入一个 concurrent.futures.Executor(如 ThreadPoolExecutorProcessPoolExecutor)或 None(表示使用默认线程池)[21]。示例:

    python 复制代码
    import asyncio
    import concurrent.futures
    
    def cpu_bound(x):
        return sum(i*i for i in range(10**6)) + x
    
    async def main():
        loop = asyncio.get_running_loop()
        # 默认线程池
        res1 = await loop.run_in_executor(None, cpu_bound, 10)
        print("线程池结果:", res1)
        # 自定义线程池
        with concurrent.futures.ThreadPoolExecutor() as pool:
            res2 = await loop.run_in_executor(pool, cpu_bound, 20)
            print("自定义线程池结果:", res2)
        # 自定义进程池
        with concurrent.futures.ProcessPoolExecutor() as pool:
            res3 = await loop.run_in_executor(pool, cpu_bound, 30)
            print("进程池结果:", res3)
    
    asyncio.run(main())

    以上示例中,cpu_bound 为耗时计算函数。run_in_executor 将其运行在线程池或进程池中,并异步等待结果[44]

与线程池/进程池的交互(concurrent.futures

Python 的标准库 concurrent.futures 提供了线程池(ThreadPoolExecutor)和进程池(ProcessPoolExecutor)。在异步代码中,可通过 loop.run_in_executor() 将阻塞调用提交给这些池。上节示例展示了如何使用默认线程池、指定线程池或进程池来运行函数。Python 3.11 还引入了 InterpreterPoolExecutor,允许在多个 Python 解释器进程中并行运行(适用于释放 GIL 的场景)[45]

此外,如果从另一个线程想要向事件循环提交协程任务,可以使用 asyncio.run_coroutine_threadsafe(coro, loop)。该函数将协程提交到指定事件循环,并返回一个 concurrent.futures.Future,让调用线程等待结果[46]。通常只有在线程间协调时需要用到。

性能注意事项与常见陷阱

  • I/O 绑定 vs CPU 绑定asynciothreading 等适用于 I/O 密集型任务,而对于 CPU 密集型任务(大量计算)需要谨慎。由于 Python 的全局解释器锁(GIL),同一线程中协程仍是串行执行,无法利用多核并行[18]。因此,CPU 绑定任务最好使用进程池或 C 扩展释放 GIL[47][44]。另一方面,如果任务中包含大量的 await(I/O 等待),使用 asyncio 能显著提升吞吐。

  • 避免阻塞事件循环 :任何会阻塞线程的操作(如 time.sleep、同步文件/网络 I/O 等)都不应在协程中直接调用,否则会挂起整个事件循环。应使用异步版本(如 await asyncio.sleep、异步文件库)或将其移入线程池(asyncio.to_thread / run_in_executor[43][21]

  • 任务调度开销:创建大量短小任务可能会有调度开销,短任务的串行执行有时比大量切换效率更高。合理使用并发原语(如限制并发数的信号量、任务组等)可以避免过度并发。

  • 取消与清理 :被取消的协程如果在中间层捕获了 CancelledError,可能会导致一些高级并发结构(如 asyncio.TaskGroup)无法正常工作[36]。一般应确保在捕获 CancelledError 后重新抛出。建议在协程中使用 try/finally 做清理,而不是吞掉取消异常[36]

  • 引用管理 :正如前述,如果不保留对 Task 的引用,可能导致任务提前结束或被回收[19]。对于"火并忘"类型的后台任务,务必将其保存到全局集合或使用任务组(asyncio.TaskGroup)管理。

  • 调试注意 :异步代码的调试相比同步更加复杂,可考虑开启调试模式(loop.set_debug(True) 或环境变量 PYTHONASYNCIODEBUG=1),及时捕捉未处理的异常和资源泄漏。

总之,异步编程并不适合所有场景。在真正需要同时处理大量网络/磁盘 I/O 时,asyncio 可以大幅提升性能;但对于简单的顺序流程或以计算为主的任务,其优势有限[18]。理解其工作机制、避免常见陷阱,才能编写高效、正确的异步代码。


参考引用:

[0] 理解同步异步与阻塞非阻塞------傻傻分不清楚的终极指南-腾讯云开发者社区-腾讯云

[1] 理解同步异步与阻塞非阻塞------傻傻分不清楚的终极指南-腾讯云开发者社区-腾讯云

[2] 理解同步异步与阻塞非阻塞------傻傻分不清楚的终极指南-腾讯云开发者社区-腾讯云

[3] 理解同步异步与阻塞非阻塞------傻傻分不清楚的终极指南-腾讯云开发者社区-腾讯云

[4] Coroutines and Tasks --- Python 3.14.2 documentation

[5] Coroutines and Tasks --- Python 3.14.2 documentation

[6] Coroutines and Tasks --- Python 3.14.2 documentation

[7] Coroutines and Tasks --- Python 3.14.2 documentation

[8] Python's asyncio: A Hands-On Walkthrough -- Real Python

[9] Coroutines and Tasks --- Python 3.14.2 documentation

[10] Coroutines and Tasks --- Python 3.14.2 documentation

[11] Event Loop --- Python 3.14.2 documentation

[12] Event Loop --- Python 3.14.2 documentation

[13] Event Loop --- Python 3.14.2 documentation

[14] Coroutines and Tasks --- Python 3.14.2 documentation

[15] Coroutines and Tasks --- Python 3.14.2 documentation

[16] Coroutines and Tasks --- Python 3.14.2 documentation

[17] Coroutines and Tasks --- Python 3.14.2 documentation

[18] Python's asyncio: A Hands-On Walkthrough -- Real Python

[19] Coroutines and Tasks --- Python 3.14.2 documentation

[20] Coroutines and Tasks --- Python 3.14.2 documentation

[21] Event Loop --- Python 3.14.2 documentation

[22] Coroutines and Tasks --- Python 3.14.2 documentation

[23] Coroutines and Tasks --- Python 3.14.2 documentation

[24] Coroutines and Tasks --- Python 3.14.2 documentation

[25] Coroutines and Tasks --- Python 3.14.2 documentation

[26] Coroutines and Tasks --- Python 3.14.2 documentation

[27] Coroutines and Tasks --- Python 3.14.2 documentation

[28] Coroutines and Tasks --- Python 3.14.2 documentation

[29] Streams --- Python 3.14.2 documentation

[30] Streams --- Python 3.14.2 documentation

[31] Streams --- Python 3.14.2 documentation

[32] contextlib --- Utilities for with-statement contexts --- Python 3.14.2 documentation

[33] contextlib --- Utilities for with-statement contexts --- Python 3.14.2 documentation

[34] Asynchronous Iterators and Iterables in Python -- Real Python

[35] Coroutines and Tasks --- Python 3.14.2 documentation

[36] Coroutines and Tasks --- Python 3.14.2 documentation

[37] Synchronization Primitives --- Python 3.14.2 documentation

[38] Synchronization Primitives --- Python 3.14.2 documentation

[39] Queues --- Python 3.14.2 documentation

[40] Queues --- Python 3.14.2 documentation

[41] Coroutines and Tasks --- Python 3.14.2 documentation

[42] Coroutines and Tasks --- Python 3.14.2 documentation

[43] Coroutines and Tasks --- Python 3.14.2 documentation

[44] Event Loop --- Python 3.14.2 documentation

[45] Event Loop --- Python 3.14.2 documentation

[46] Coroutines and Tasks --- Python 3.14.2 documentation

[47] Python's asyncio: A Hands-On Walkthrough -- Real Python

相关推荐
ycydynq2 小时前
django 数据库 多表操作
数据库·python·django
m0_549416662 小时前
自动化与脚本
jvm·数据库·python
查无此人byebye2 小时前
手写Multi-Head Attention多头注意力机制,Pytorch实现与原理详解
人工智能·pytorch·python·深度学习·transformer
克里斯蒂亚诺更新2 小时前
vue展示node express调用python解析tdms
服务器·python·express
idwangzhen2 小时前
2026郑州GEO优化哪个平台靠谱
python·信息可视化
计算机毕业编程指导师2 小时前
【Python大数据选题】基于Hadoop+Spark奥运会金牌榜可视化分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习 数据挖掘
大数据·hadoop·python·计算机·spark·毕业设计·奥运会金牌
hhcgchpspk2 小时前
python实现音频淡入淡出功能
python·程序人生·音视频·pygame
曲幽2 小时前
FastAPI异步多线程:从踩坑到精通,解锁高性能API的正确姿势
python·flask·fastapi·web·thread·async·httpx·asyncio
高洁012 小时前
知识图谱在装备领域应用场景
python·深度学习·机器学习·数据挖掘·知识图谱