- Published on
考虑 Python asyncio 异步编程之前,应该注意哪些问题?
- Authors
- Name
- 浩森 Hansen
并行和并发
编程中的函数,大家都熟悉。一个函数可以表示一段机器执行过程,而 async 异步函数,指的就是可以被调度器异步调度的函数。
在说异步之前,先搞清楚并行、并发这个两比较容易混淆的概念:
- 并行:指的是在同一时间内,多个任务同时一起运行。
- 并发:指的是不同的任务之间没有明确的先后执行关系。任务可以交替先后执行,也可以同时一起执行。
也就是说,现在有两个任务 A 和 B,如果他们之间没有明确的先后顺序(例如没有要求 A 跑完后 B 才能开始),那么 A、B 就可以并发执行。
在单线程执行的情况下,并发的 A、B 可以交替运行,例如 A 先跑一半,然后再跑 B,最后再把 A 跑完。
如果在多线程情况下,A、B 可以同时运行,速度翻一倍。就像马路上并排行驶的两辆车,同时向一个方向齐头并进。
也许你已经发现了并发和并行的区别了:
以上情况,A 和 B 是两个可并发的任务:在单线程中 A、B 交替运行,但不并行;多线程下,A、B 并行了。
所以,我认为并发和并行最本质的区别是:【并发】是描述任务(函数)性质的,而【并行】是描述执行情况的。
从概念上来看,并行的前提是任务可以并发,所以并行要满足的条件更多。
在 Python asyncio 中的异步编程,一般指的是并发编程。
明确以上概念,就可以继续讨论了。
异步函数-协程
一段示例代码:
import asyncio
import aiohttp
# 定义一个异步函数模拟网络请求
async def fetch_data(url, delay):
print(f"⏳ 开始请求 {url} (延迟 {delay}s)")
# 模拟网络延迟(非阻塞等待)
await asyncio.sleep(delay)
# 实际项目中这里可以替换为真正的网络请求
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
content = await response.text()
print(f"✅ {url} 请求完成,响应长度: {len(content)} 字节")
return content
用 async def
声明的函数,就是一个异步函数(协程),代表了一个可并发执行的任务。
一个典型的 async 函数里面,往往都有 await
标记。这就像高速公路上的路牌,标记在这个地方协程进入等待状态,并让出执行权。
async 函数,不是一个普通的 Python 函数,在执行时 Python 会对其做一些封装。如果我们直接调用它获得的返回值是一个 Coroutine
对象。无法直接使用。
那么应该如何使用 async 函数呢?
执行器和事件循环
async 函数必须使用特定的调度器调用。最简单的,就是 asyncio.run()
。
asyncio.run()
会创建一个事件循环,并且在事件循环中执行 async 函数。当然 asyncio 还提供了很多其他方法,可以在更细的颗粒度上操作调度器。
不论怎么样,你只需要记住:async 异步函数必须在一个事件循环内下才能正常执行即可。
异步编程的局限性
异步编程确实让并发开发变得容易,在实际使用的过程中,还是会有不小的局限性:
首先是三方库之间的兼容性问题,像 FastAPI 和 APScheduler 都支持异步编程的事件循环调度。但是两个库一起使用的时候,他们会把所有任务放在同一个事件循环中运行。
这就会造成很多奇怪的问题,例如 APScheduler 的定时任务不执行,或者执行的长任务占用太久,导致 FastAPI 的 Router 无响应。这么一来,两个模块相互影响,违背了低耦合原则。
其次是异步编程的生态完整性问题,一旦使用异步编程,那么就要保证所有使用的第三方库都应该支持异步调用。例如数据库、http 请求访问等。如果调用的库不支持异步方法,那么就要用多线程来异步化。
一旦用到多线程,编程就变得复杂了。原本使用异步编程是为了写并发函数更方便,但是这样一来,反而把事情复杂化了,那么我为什么不一开始就用多线程来完成异步呢?
总结
异步编程是个挺有用的技术,但是就像其他技术一样,都有其适用范围。有优势的同时,也有其局限性。
总的来说,一旦使用异步,那么整个项目中的生态,不论是上游中间件还是下游三方库,都要异步化。这里的工作量还是挺大的。
如果更多考虑通用的兼容性,那么异步编程可能并不适合你。
This work is licensed under Creative Commons Attribution-NonCommercial 4.0 International