Python asyncio 模块

Coroutines, the event loop, gather vs create_task, timeouts and the async/await mental model.

Advanced
Key PointsAsync gives concurrency on a single thread: await yields control while waiting, so never call blocking code inside a coroutine.

Mental Model

async def defines a coroutine; calling it returns an awaitable that does nothing until awaited or scheduled. One event loop drives many coroutines, switching at each await.

Python
import asyncio


async def fetch(name, delay):
    print("start", name)
    await asyncio.sleep(delay)        # 让出控制权,不阻塞线程
    print("end", name)
    return name


async def main():
    await fetch("a", 1)               # 串行:总耗时约 1s
    await asyncio.gather(fetch("b", 1), fetch("c", 1))   # 并发:总耗时约 1s


asyncio.run(main())

gather vs create_task

ConstructBehaviourFailure mode
await f()Runs inline, serialPropagates immediately
asyncio.gather(a, b)Run concurrently, collect resultsFirst error propagates, others keep running
asyncio.create_task(c)Schedule now, await laterUn-awaited exceptions are logged
asyncio.TaskGroup()3.11+ structured concurrencyCancels siblings on failure
Python
async def main():
    # 并发 + 结果按传入顺序返回
    a, b = await asyncio.gather(fetch("x", 1), fetch("y", 2))

    # 先调度,后等待(可以中间做别的事)
    task = asyncio.create_task(fetch("z", 1))
    print("doing something else")
    result = await task

    # 3.11+:任一失败则整组取消
    async with asyncio.TaskGroup() as tg:
        tg.create_task(fetch("p", 1))
        tg.create_task(fetch("q", 1))

Timeouts

Python
async def main():
    try:
        async with asyncio.timeout(2):        # 3.11+
            await fetch("slow", 5)
    except TimeoutError:
        print("timed out")

    # 3.8+ 兼容写法
    # await asyncio.wait_for(fetch("slow", 5), timeout=2)

Do Not Block the Loop

Python
import asyncio
import time


async def bad():
    time.sleep(2)              # 阻塞整个事件循环,所有协程停摆


async def good():
    await asyncio.sleep(2)     # 让出控制权

# 必须调同步库时,丢到线程池
async def run_blocking():
    loop = asyncio.get_running_loop()
    return await loop.run_in_executor(None, time.sleep, 2)
A single blocking call (requests, time.sleep, heavy CPU) inside a coroutine serialises your entire service. Use an async client (httpx/aiohttp) or run_in_executor.

Friends & Partners