正在加载,请稍候…

Python Async Programming: Mastering asyncio for High-Performance Applications

Deep dive into Python asyncio. Coroutines, event loops, tasks, semaphores, async context managers, and building high-performance async scrapers and API clients.

asyncio enables massive IO-bound concurrency on a single thread. Here's how to use it correctly.

The Mental Model

One thread, cooperative multitasking, explicit yield with await. IO-bound workloads see 10-100x speedups; CPU-bound needs multiprocessing.

import asyncio, aiohttp

# BAD: blocks entire event loop
async def fetch_bad(url: str) -> str:
    import requests
    return requests.get(url).text  # Blocks all other coroutines

# GOOD: non-blocking
async def fetch_good(session: aiohttp.ClientSession, url: str) -> str:
    async with session.get(url) as response:
        return await response.text()

Concurrent Requests

async def check_all(urls: list) -> list:
    async with aiohttp.ClientSession() as session:
        tasks = [get_status(session, url) for url in urls]
        return await asyncio.gather(*tasks, return_exceptions=True)

Semaphores (Rate Control)

async def scrape_urls(urls: list, max_concurrent: int = 20) -> list:
    sem = asyncio.Semaphore(max_concurrent)

    async def fetch(url):
        async with sem:
            async with aiohttp.ClientSession() as s:
                async with s.get(url) as resp:
                    return await resp.text()

    return await asyncio.gather(*[fetch(u) for u in urls], return_exceptions=True)

Producer-Consumer Queue

async def pipeline(urls: list) -> list:
    queue, results, N = asyncio.Queue(maxsize=100), [], 5

    async def producer():
        for url in urls:
            await queue.put(url)
        for _ in range(N):
            await queue.put(None)  # Poison pills

    async def consumer():
        async with aiohttp.ClientSession() as session:
            while True:
                url = await queue.get()
                if url is None:
                    queue.task_done()
                    break
                try:
                    async with session.get(url) as resp:
                        results.append({'url': url, 'len': len(await resp.text())})
                finally:
                    queue.task_done()

    await asyncio.gather(producer(), *[asyncio.create_task(consumer()) for _ in range(N)])
    return results

CPU-Bound Work with ProcessPoolExecutor

from concurrent.futures import ProcessPoolExecutor
import multiprocessing

def heavy_compute(data: list) -> dict:
    return {'result': sum(x**2 for x in data)}

async def run_heavy(data: list) -> dict:
    loop = asyncio.get_event_loop()
    with ProcessPoolExecutor(max_workers=multiprocessing.cpu_count()) as ex:
        return await loop.run_in_executor(ex, heavy_compute, data)

Error Handling with Retry

async def with_retry(coro_fn, max_attempts=3, base_delay=1.0):
    for attempt in range(max_attempts):
        try:
            return await coro_fn()
        except (aiohttp.ClientError, asyncio.TimeoutError) as e:
            if attempt < max_attempts - 1:
                await asyncio.sleep(base_delay * (2 ** attempt))
            else:
                raise

asyncio rewards developers who respect its limits: IO-bound work, controlled concurrency, and CPU work in executors.

→ Test your async API endpoints with the URL Parser tool.