跳转至

异步编程示例

演示 asyncio 的核心用法:协程、并发任务编排(gather)、信号量限流、 异步生成器与超时控制。这些能力是后续构建高并发 Agent 服务的基础。 对应教程:第2卷《Python 进阶》第3章 asyncio。

文件路径: examples/volume-02-python\async_demo.py


源代码

"""异步编程示例。

演示 ``asyncio`` 的核心用法:协程、并发任务编排(``gather``)、信号量限流、
异步生成器与超时控制。这些能力是后续构建高并发 Agent 服务的基础。

对应教程:第2卷《Python 进阶》第3章 asyncio。
"""

from __future__ import annotations

import asyncio
import time
from collections.abc import AsyncIterator
from dataclasses import dataclass, field


@dataclass
class FetchResult:
    """模拟一次异步获取的结果。

    Attributes:
        name: 任务名。
        duration: 实际耗时(秒)。
        value: 返回值。
    """

    name: str
    duration: float
    value: str


async def fake_fetch(name: str, delay: float, value: str | None = None) -> FetchResult:
    """模拟一个耗时的异步 IO 操作(如调用 LLM API)。

    Args:
        name: 任务名。
        delay: 模拟耗时(秒)。
        value: 返回值,默认等于 ``name``。

    Returns:
        ``FetchResult``。
    """
    start = time.monotonic()
    await asyncio.sleep(delay)
    elapsed = time.monotonic() - start
    return FetchResult(name=name, duration=elapsed, value=value if value is not None else name)


async def gather_all(tasks: list[tuple[str, float, str | None]]) -> list[FetchResult]:
    """并发执行多个获取任务并返回结果列表。

    Args:
        tasks: ``(name, delay, value)`` 三元组列表。

    Returns:
        按入参顺序返回的 ``FetchResult`` 列表。
    """
    coros = [fake_fetch(n, d, v) for n, d, v in tasks]
    return await asyncio.gather(*coros)


async def gather_with_semaphore(
    tasks: list[tuple[str, float, str | None]],
    limit: int,
) -> list[FetchResult]:
    """使用信号量限制并发度的并发执行。

    Args:
        tasks: ``(name, delay, value)`` 三元组列表。
        limit: 最大并发数。

    Returns:
        ``FetchResult`` 列表。
    """
    semaphore = asyncio.Semaphore(limit)

    async def _run(name: str, delay: float, value: str | None) -> FetchResult:
        async with semaphore:
            return await fake_fetch(name, delay, value)

    return await asyncio.gather(*[_run(n, d, v) for n, d, v in tasks])


async def stream_items(count: int, delay: float = 0.01) -> AsyncIterator[str]:
    """异步生成器:逐项产出,模拟流式响应。

    Args:
        count: 产出数量。
        delay: 每项间隔(秒)。

    Yields:
        形如 ``item-0`` 的字符串。
    """
    for i in range(count):
        await asyncio.sleep(delay)
        yield f"item-{i}"


async def collect_stream(count: int, delay: float = 0.01) -> list[str]:
    """收集 ``stream_items`` 的全部产出。

    Args:
        count: 产出数量。
        delay: 每项间隔。

    Returns:
        字符串列表。
    """
    return [item async for item in stream_items(count, delay)]


async def fetch_with_timeout(name: str, delay: float, timeout: float) -> FetchResult | None:
    """带超时的获取,超时返回 ``None``。

    Args:
        name: 任务名。
        delay: 模拟耗时。
        timeout: 超时阈值。

    Returns:
        ``FetchResult`` 或 ``None``(超时)。
    """
    try:
        return await asyncio.wait_for(fake_fetch(name, delay), timeout=timeout)
    except asyncio.TimeoutError:
        return None


@dataclass
class AsyncCounter:
    """简单的异步计数器,演示 ``asyncio.Lock`` 保护共享状态。

    Attributes:
        value: 当前计数值。
    """

    value: int = 0
    _lock: asyncio.Lock = field(default_factory=asyncio.Lock, repr=False)

    async def increment(self, n: int = 1) -> int:
        """原子地自增。

        Args:
            n: 增量。

        Returns:
            自增后的值。
        """
        async with self._lock:
            self.value += n
            return self.value


async def main() -> None:
    """演示 asyncio 各类用法。"""
    print("== 并发 gather ==")
    results = await gather_all([("a", 0.05, "A"), ("b", 0.02, "B"), ("c", 0.03, "C")])
    for r in results:
        print(f"  {r.name} -> {r.value} ({r.duration:.3f}s)")

    print("\n== 限流并发 (limit=2) ==")
    tasks = [(f"t{i}", 0.05, None) for i in range(5)]
    start = time.monotonic()
    limited = await gather_with_semaphore(tasks, limit=2)
    print(f"  完成 {len(limited)} 个,总耗时 {time.monotonic() - start:.3f}s")

    print("\n== 流式产出 ==")
    items = await collect_stream(3, delay=0.01)
    print(f"  {items}")

    print("\n== 超时控制 ==")
    ok = await fetch_with_timeout("fast", 0.01, timeout=0.5)
    slow = await fetch_with_timeout("slow", 0.5, timeout=0.05)
    print(f"  fast: {ok.value if ok else None}")
    print(f"  slow: {slow}")


if __name__ == "__main__":
    asyncio.run(main())