You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于CPython的Python asyncio原子执行单元技术问询

Python asyncio 的原子执行单元是什么?

在asyncio的协作式调度模型里,协程的执行会一直持续到遇到显式的异步操作(比如await、asyncio.sleep()、asyncio.wait()等)才会暂停,这段从一个异步让出点到下一个异步让出点之间的代码,就是asyncio提供的原子执行单元——也就是说,这段代码会被完整执行,不会被其他协程打断。

为什么是这样?

asyncio基于单线程事件循环运行,协程之间是协作式调度,而非线程的抢占式调度:

  • 线程依赖GIL,在每个字节码执行完成后都可能被调度切换,所以单个字节码是原子的,但跨多个字节码的操作可能被打断。
  • 协程则完全不同,只有当代码主动调用await这类会触发事件循环调度的操作时,当前协程才会让出CPU,让其他协程运行。在没有这类操作的代码段里,不管包含多少字节码,都会一次性执行完毕,不会被其他协程干扰。

举个例子

下面的代码中,increment函数里没有任何await操作,所以每个协程的temp = x和x = temp +1会被完整执行,最终结果一定是1000:

import asyncio

x = 0

async def increment():
    global x
    temp = x
    x = temp + 1

async def main():
    tasks = [increment() for _ in range(1000)]
    await asyncio.gather(*tasks)
    print(x)  # 输出 1000

asyncio.run(main())

但如果在temp = x之后加一行await asyncio.sleep(0)(主动让出控制权),结果就会变成不确定的数值——因为协程在执行到await时暂停,其他协程会修改x,破坏了操作的原子性:

import asyncio

x = 0

async def increment():
    global x
    temp = x
    await asyncio.sleep(0)  # 主动让出,原子性被打破
    x = temp + 1

async def main():
    tasks = [increment() for _ in range(1000)]
    await asyncio.gather(*tasks)
    print(x)  # 输出小于1000的随机数

asyncio.run(main())

注意事项

不要依赖这种原子执行单元来处理竞态条件,因为一旦代码中不小心引入了await(哪怕是调用了其他含await的函数),原子性就会被打破。正确的做法是使用asyncio提供的同步原语,比如asyncio.Lock,来保护共享资源的访问。

内容的提问来源于stack exchange,提问作者김꼬막

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 20:01:10