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

泛型List的RemoveRange方法线程安全性问题咨询

List.RemoveRange在多线程场景下的线程安全性问题

结论先行:你这个场景下的RemoveRange完全不是线程安全的,会导致各种不可预料的问题

问题根源

  1. 线程1的操作完全无同步:线程2的Add加了锁,但线程1从获取Count到执行RemoveRange的全流程都没加锁,两个线程的操作完全没有互斥约束。
  2. List本身非线程安全:所有修改List内部结构的方法(包括Add、RemoveRange)都没有内置线程安全机制,并发读写会直接破坏List的内部数组状态。

你举例场景的可能后果

假设线程1一开始拿到Count=10,处理过程中线程2成功添加1个元素,List的Count变为11:

  • 最直接的问题:线程1执行RemoveRange(0,10)会删除前10个元素,刚添加的第11个元素会被保留,完全不符合“处理10个就删10个”的预期。
  • 更严重的情况:如果线程2的Add刚好发生在RemoveRange执行过程中(比如RemoveRange正在移动数组元素时),List的内部结构会被彻底破坏,可能抛出InvalidOperationException、IndexOutOfRangeException,甚至导致后续所有对List的操作返回错误结果(元素乱序、丢失等)。

解决方案

既然你需要有序性不想用ConcurrentBag,最可靠的方案是给所有涉及List读写的操作加上同一把锁,同时尽量缩小锁的范围:

线程1修改后代码:

lock(this.list) // 和线程2使用同一锁对象,也可单独声明专用锁变量
{
    int batchCount = this.list.Count;
    // 先把待处理元素复制出来,避免耗时操作占用锁
    var batch = this.list.GetRange(0, batchCount);
    this.list.RemoveRange(0, batchCount);
}
// 在这里执行你的处理逻辑,用复制好的batch

线程2代码保持不变(确保锁对象一致):

lock(this.list)
{
    this.list.Add(item);
}

如果处理逻辑耗时较长,务必不要将其放在锁内,只通过锁保护List的读写操作即可,避免锁持有时间过长影响性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 22:25:42