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

71 lines
2.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Python Async: Beyond the Basics"
date: 2026-05-12T11:00:00+08:00
draft: false
poet: |
They also serve who only stand and wait.
——John Milton,英国,1608–1674
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.