协程核心在于理解async/await在事件循环中的调度行为,而非课时积累;需避免同步阻塞、正确复用session、区分gather与create_task用途,并厘清执行权归属。

这不是第538讲,也没有所谓“系统学习路线”的固定编号。协程不是靠刷课数积累的,而是靠理解 async/await 在事件循环中的实际行为、调度时机和资源约束。
协程不等于“多线程提速”,它解决的是 I/O 等待期间的 CPU 闲置问题;一旦混入 CPU 密集型操作,asyncio 不但不加速,反而拖慢。
为什么 async def 函数返回的是 coroutine 对象,而不是直接执行?
因为 Python 的协程是“可暂停、可恢复”的函数对象,必须显式交给事件循环驱动。直接调用 my_coro() 只是创建协程对象,不触发任何逻辑。
- 正确启动方式:用
await my_coro()(在另一个协程内),或asyncio.run(my_coro())(顶层入口) - 错误写法:
my_coro()—— 这会漏掉RuntimeWarning: coroutine 'xxx' was never awaited,且什么都不会发生 - 调试技巧:打印
type(my_coro())确认是,不是None或结果值
asyncio.gather() 和 asyncio.create_task() 的关键区别在哪?
前者是并发“批量等待”,后者是立即调度并返回 Task 对象用于后续控制(如取消、检查状态)。
立即学习“Python免费学习笔记(深入)”;
-
asyncio.gather(a(), b(), c()):等全部完成才返回结果列表,任一异常即中断(除非加return_exceptions=True) -
task = asyncio.create_task(a()):立刻把a()丢进事件循环队列,当前协程可继续执行其他逻辑,之后再await task - 常见误用:在循环里反复
await asyncio.create_task(...)—— 这等于串行,失去并发意义
HTTP 请求用 aiohttp 时,为什么连接池没生效?
默认情况下,aiohttp.ClientSession() 每次新建都创建独立连接池。复用 session 才能真正复用 TCP 连接和 DNS 缓存。
import aiohttp import asyncio❌ 错误:每次请求都新建 session,连接无法复用
async def bad_request(): async with aiohttp.ClientSession() as session: async with session.get('https://www.php.cn/link/5f69e19efaba426d62faeab93c308f5c') as resp: return await resp.text()
✅ 正确:session 复用,连接池起作用
async def good_request(session): async with session.get('https://www.php.cn/link/5f69e19efaba426d62faeab93c308f5c') as resp: return await resp.text()
async def main(): async with aiohttp.ClientSession() as session: await asyncio.gather( good_request(session), good_request(session), good_request(session) )
协程中调用同步函数(如 time.sleep()、json.loads())会怎样?
整个事件循环会被阻塞 —— 其他所有协程停摆,直到该同步调用返回。这不是“异步”,是假并发。
- CPU 密集型:用
loop.run_in_executor()扔到线程池(注意线程安全) -
标准库阻塞调用(如
time.sleep):必须换为await asyncio.sleep() - 第三方同步库(如某些数据库驱动):找对应异步版本(
aiomysql、asyncpg),别硬套run_in_executor
真正卡住人的从来不是语法,而是没想清楚“谁在等谁”“哪段代码实际占着事件循环”“I/O 完成后回调是否被正确挂起”。协程出问题,90% 是调度逻辑没理清,不是写错了 await。










