Add infinite-scroll pagination to home page and publish new blog posts
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
---
|
||||
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.
|
||||
Reference in New Issue
Block a user