国际访客建议访问 Primers 编程伙伴 国际服 以获得更好的体验。 快速访问 Python 输入输出 异步IO

# Python 的异步 I/O

异步 I/O(Asynchronous Input/Output) 是一种 不阻塞程序执行 的 I/O 操作方式。发起 I/O 操作后,不必等待操作完成,程序可以继续做其他事情;当操作完成时,通过回调、事件循环或协程机制通知程序处理结果。

Python 的异步 I/O 基于网络 API 实现,本站使用的在线运行环境 Shift 因 WASM 安全性限制,不支持网络操作,因此无法运行异步 I/O 程序。

# 协程

Python 的异步 I/O 基于协程实现。使用async关键字来创建一个异步函数,对异步函数的调用不会执行该函数,而是生成一个协程对象。

对每一个协程对象,都必须等待其结束(即使是没有启动的协程),否则会产生一个 RuntimeWarning 类型的异常。

示例 :

要启动一个协程,有三种方式:

  • 通过 asyncio.run 运行一个协程对象

  • 使用 await 关键字,这种方法只能在另一个 async 函数中才能使用

  • 通过 asyncio.create_task

  • await 必须在 async 函数中才能使用,因此无法启动协程的顶层入口点,此时只能使用 asyncio.run 函数。

  • await 让出当前协程并运行目标协程,当前协程直到目标目标的状态变为 done 时才会恢复就绪。 如果 await 的目标不是一个协程(例如Task和Future),让出当前协程后,事件循环(EventLoop)会从就绪队列中选择一个协程运行。

  • asyncio.create_task 让出当前协程并运行目标协程,当前协程不会等待而是加入就绪队列。
    只要目标协程让出,当前协程就有机会执行,从而将启动多个协程,实现并发执行。
    返回的 Task 对象也可以在适当的时候使用 await 等待其结束。

协程的状态转移:

digraph G {
    rankdir=LR;
    node [shape=box, style=filled, color=lightblue];
    
    // 定义节点
    ready [label="就绪"]; 
    run [label="运行"];  
    wait [label="等待"];
    exit [label="退出"];

    ready -> run [label="调度"];
    run -> wait [label="await"];
    run -> ready [label="create_task"];
    run -> exit [label="运行完毕"];
    wait -> ready [label="等待的协程退出"];
}

awai 示例:

import asyncio
import time

async def say_hello():
    print("hello", time.strftime('%X'))
    await asyncio.sleep(1)
    print("hello", time.strftime('%X'))

async def say_world():
    print("world", time.strftime('%X'))
    await asyncio.sleep(1)
    print("world", time.strftime('%X'))

# 顶层入口点
async def main():
    await say_hello() # 启动say_hello()返回的协程,并等待其结束
    await say_world() # 要等到前一个await结束后,才会启动

# 启动顶层入口点
asyncio.run(main())

运行结果 :

hello 15:27:26
hello 15:27:27
world 15:27:27
world 15:27:28

asyncio.create_task 示例 :

import asyncio
import time

async def say_hello():
    print("hello", time.strftime('%X'))
    await asyncio.sleep(1)
    print("hello", time.strftime('%X'))

async def say_world():
    print("world", time.strftime('%X'))
    await asyncio.sleep(1)
    print("world", time.strftime('%X'))

# 顶层入口点
async def main():
    task_say_hello = asyncio.create_task(say_hello()) # 启动协程不等待
    task_say_world = asyncio.create_task(say_world()) 

    await task_say_hello
    await task_say_world

# 启动顶层入口点
asyncio.run(main())

运行结果 :

hello 15:29:41
world 15:29:41
hello 15:29:42
world 15:29:42

通过上面两个示例打印的顺序和时间可以看出 awaitasyncio.create_task 的区别:

  • await 启动另一个协程时,使当前协程进入等待状态,需要启动的协程运行完毕,当前协程才能恢复就绪,因此运行流程是串行的

  • asyncio.create_task 启动另一个协程时,当前协程仍然是就绪状态,因此运行流程是并发的。

本文 更新于: 2025-05-31 19:53:12 创建于: 2025-05-31 19:53:12