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

asyncio中如何安全实现共享列表的元素移除操作

asyncio 场景下共享列表安全增删/遍历实现方案

问题本质

asyncio 是单线程协作式并发模型,协程只会在 await 位置主动让出事件循环控制权。如果遍历共享列表时碰到await点让出控制权,其他协程刚好对列表做增删操作修改了列表结构,就会直接抛出RuntimeError: list changed size during iteration异常。

可选实现方案

根据业务对一致性、性能的要求,可以选以下三种常用方案:

方案1:遍历前拷贝列表副本(实现成本最低)

遍历操作前先对原列表做一次浅拷贝,后续遍历完全基于副本执行,原列表的任何增删都不会影响当前遍历流程。
示例代码:

a = [] # List[Connection]

async def foo():
    # list(a) 生成原列表的浅拷贝,仅拷贝列表内的元素引用,开销极低
    for v in list(a):
        await v.send('msg')

async def bar():
    await SomeAsyncFunc()
    # 直接修改原列表不会影响已经开始的遍历操作
    a.pop(0)

适用场景:遍历过程不需要感知列表新增的元素,对数据一致性要求不高,想要最低改造成本。

方案2:用 asyncio.Lock 做临界区互斥(一致性最强)

用asyncio提供的协程级锁,把所有涉及列表结构修改、遍历的操作都纳入锁保护,保证同一时间只有一个协程操作列表结构。
注意不要把长耗时的await操作放在锁持有阶段,避免锁粒度过长阻塞其他协程,可以先在锁内拷贝出要操作的对象,释放锁后再执行耗时逻辑:

import asyncio
a = [] # List[Connection]
list_op_lock = asyncio.Lock()

async def foo():
    # 仅在拷贝列表的极短时间内持有锁
    async with list_op_lock:
        send_targets = list(a)
    # 释放锁后再执行耗时的消息发送,不阻塞其他协程增删列表
    for v in send_targets:
        await v.send('msg')

async def bar():
    await SomeAsyncFunc()
    # 修改列表结构时必须持有锁
    async with list_op_lock:
        a.pop(0)

适用场景:对列表数据一致性要求高,需要保证遍历/增删操作的原子性。注意不要使用多线程的threading.Lock,会阻塞整个事件循环。

方案3:标记删除替代即时删除(高并发场景性能最优)

不直接在协程中修改列表结构,所有删除操作先给对应元素打删除标记,遍历的时候自动跳过已标记删除的元素,等遍历结束或者低峰期再统一清理列表里的无效元素。
示例代码:

a = [] # List[Connection]

async def foo():
    for v in a:
        # 跳过已标记为关闭的连接
        if getattr(v, "is_closed", False):
            continue
        await v.send('msg')
    # 遍历完成后一次性清理所有已标记删除的元素
    a[:] = [v for v in a if not getattr(v, "is_closed", False)]

async def bar():
    await SomeAsyncFunc()
    # 不修改列表结构,仅给待删除元素打标记
    a[0].is_closed = True

适用场景:列表增删操作非常频繁,能接受删除操作有短暂延迟,想要最高的并发性能。

注意事项

  • 列表浅拷贝仅复制列表内存储的对象引用,不会深度拷贝对象本身,万级元素以内的拷贝开销可以忽略
  • 所有涉及列表长度变化的操作(append/pop/insert/切片赋值等)都属于结构修改,需要和遍历操作做隔离
  • 不要在持有锁的阶段执行无超时的网络IO、长耗时计算,会拖慢整个事件循环的调度效率

内容的提问来源于stack exchange,提问作者bin381

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 23:01:10