Files
Hugo/content/blog/python-async-guide.md
T

68 lines
2.6 KiB
Markdown

---
title: "Python Async: Beyond the Basics"
date: 2026-05-12T11:00:00+08:00
draft: false
description: "Understanding the event loop, writing proper async context managers, and avoiding the most common pitfalls with asyncio.gather and cancel scopes."
tags:
- Python
- Async
- Programming
---
Async Python is no longer optional if you're doing any kind of I/O-bound work. Beyond the basics of `async`/`await`, understanding the event loop helps debug elusive issues.
## The Event Loop in Practice
The event loop runs one coroutine at a time, switching at `await` points. This is cooperative multitasking — a coroutine that never awaits blocks everything else. If you have CPU-bound work inside an async function, offload it to a thread pool with `asyncio.to_thread()`.
```python
import asyncio
# Bad: blocks the event loop
async def bad():
result = heavy_computation() # CPU-bound, no await
return result
# Good: offloads to a thread
async def good():
result = await asyncio.to_thread(heavy_computation)
return result
```
## Async Context Managers
Writing proper async context managers is essential for managing connections and locks:
```python
class AsyncConnection:
async def __aenter__(self):
self.conn = await create_connection()
return self.conn
async def __aexit__(self, *args):
await self.conn.close()
```
The `contextlib.asynccontextmanager` decorator makes this even cleaner for simple cases.
## Common Pitfalls with `asyncio.gather`
`gather` runs tasks concurrently, but one failing task doesn't cancel the others by default. If you need all-or-nothing semantics, use `asyncio.TaskGroup` (Python 3.11+):
```python
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(fetch(url1))
t2 = tg.create_task(fetch(url2))
# Both succeeded, or both were cancelled
```
## Cancel Scopes
Cancellation in asyncio is cooperative: `asyncio.CancelledError` is raised at the next `await`. If your coroutine catches and suppresses it, the task becomes uncancellable — usually a bug. Use `asyncio.shield()` sparingly to protect critical cleanup code.
## Structured Concurrency
The trend across languages (Trio in Python, Kotlin coroutines, Swift concurrency) is toward structured concurrency: every task has a clear parent scope that determines its lifetime. `TaskGroup` is Python's step in this direction. Embrace it over bare `create_task()` calls.
Async Python has matured significantly. Most of the old footguns have been addressed, but the fundamentals — understand the event loop, respect cancellation, and structure your concurrency — remain essential.