返回技术文章

Python 异步编程的几个坑

  • Python
  • 异步
  • 并发

示例文章,用于演示分页与列表排版,请替换成你自己的内容。

异步代码看起来像是把 def 改成 async def、把调用加上 await 就完事了,但真正上线之后出问题的地方往往不在语法上。

阻塞调用会把整个事件循环卡住

协程之所以能并发,是因为遇到 IO 等待时会把控制权交还事件循环。如果协程里调用了阻塞函数——比如 requests.gettime.sleep、或者一个没做异步封装的数据库驱动——事件循环就停在那一行,所有并发任务一起等待。

症状很有迷惑性:单请求延迟正常,并发一上来延迟就线性增长,看起来像是下游服务慢。要定位它,可以在压测时检查事件循环的滞后。

异步不是线程

两者的适用场景不同:

异步多线程
适合IO 密集、连接数多阻塞库无法改写
切换开销
共享状态单线程,仍要注意 await 处的交错需要锁

CPU 密集的任务两者都不合适,那要用多进程。

def hello(name):
    a = 1
    b = 2
    c = a + b
    print(f"{name}", c)
    return c

别在 await 之间假设状态没变

单线程不代表没有竞态。任何 await 都是一个潜在的交错点,前后读到的共享状态可能已经被别的任务改过。需要原子性的读改写操作,要么用锁,要么改成不含 await 的临界区。