Python异步编程的核心是协程与事件循环协同工作:协程需由事件循环调度执行,async def定义协程函数,调用返回协程对象,须通过asyncio.run()、create_task()或await触发运行;事件循环负责I/O监听、协程调度与任务管理;await表示让出控制权,仅适用于awaitable对象;常见错误包括忘记await、混用同步阻塞调用、误用time.sleep等。
Python异步编程的核心是协程(coroutine)与事件循环(event loop)的配合。协程本身不会自动运行,必须由事件循环调度执行;理解二者关系,是写出可靠异步代码的前提。
定义一个 async def 函数,只是创建了一个协程函数,调用它返回的是一个协程对象(coroutine object),不是立即执行,也不会启动异步行为。
例如:
async def fetch_data():
await asyncio.sleep(1)
return "done"
执行 fetch_data() 得到的是一个协程对象,打印出来类似 。此时什么都没发生。
要让它跑起来,得交给事件循环:
asyncio.run(fetch_data()) —— 自动创建新事件循环并运行,适合脚本入口loop.create_task(coro) 或 asyncio.create_task(coro) —— 把协程包装成任务(Task),加入当前事件循环待调度await coro —— 在另一个协程内部等待它,此时调用者协程会挂起,直到被等待的协程完成事件循环负责:监听 I/O 就绪、调度协程恢复、管理任务生命周期、处理回调和定时器。它不并发执行 Python 字节码,而是通过挂起/恢复协程实现单线程内的协作式并发。
关键事实:
asyncio.run() 每次都新建并关闭循环,不能在已有循环中重复调用asyncio.run() 会报错 “event loop is running”asyncio.get_running_loop()(推荐),而非过时的 get_event_loop()
await 表达式只能出现在协程函数中,它的作用不是“等待时间过去”,而是告诉事件循环:“我现在要等某个东西(比如网络响应、文件读取、另一个协程),请先去干别的,等它就绪了再回来叫我。”
能被 await 的对象必须是 awaitable,包括:
async def 返回值)__await__ 方法的对象(如 asyncio.Future、asyncio.Task)types.coroutine 装饰的生成器函数(较少见)普通函数、列表、字符串等不可 await,否则抛 TypeError: object xxx can't be used in 'await' expression。
新手容易卡在这几个地方:
time.sleep(2) 或 requests.get(),会阻塞整个事件循环,失去异步意义await task1(); await task2() 是串行;要用 await asyncio.gather(task1(), task2()) 或 asyncio.create_task() 并发启动
调试时可加 print(f"running at {time.time():.2f}") 观察实际执行顺序,比想象中更贴近“多任务交替推进”而非“同时运行”。