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

多线程场景下删除数组索引0元素抛出IndexOutOfRangeException但数组非空

问题原因与修复方案

问题根源

  • 竞态条件导致的非原子操作:你在判断BigArray.Count != 0和执行BigArray.RemoveAt(0)之间存在时间间隙,其他线程完全可能在这段时间里删掉最后一个元素,等当前线程执行RemoveAt时数组已经为空,直接触发索引越界异常。
  • 集合本身非线程安全:如果BigArray是普通的List<T>这类集合,它根本不支持多线程并发读写。就算用了AutoResetEvent,也没保护集合的访问操作,多个线程同时读写必然导致状态混乱。
  • 同步逻辑没管住集合修改:当前的AutoResetEvent只是协调了线程的等待和唤醒,但没控制住多个线程同时删除元素的行为。比如多个线程都判断Count>0,然后先后执行删除,最后一个线程执行时数组已经空了。

修复方案

1. 给所有集合操作加锁

把所有读写BigArray的代码(包括Count判断、取元素、RemoveAt)都放到同一个锁块里,确保这些操作是原子性的,不会被其他线程打断:

// 定义全局锁对象
private readonly object _arrayLock = new object();

// 线程逻辑中取元素的部分修改:
lock (_arrayLock)
{
    if (BigArray.Count < index + 1)
    {
        Debug.WriteLine("BigArray is smaller than " + (index + 1));
        continue;
    }
    byte[] data = BigArray[index];
}

// 删除元素的部分修改:
lock (_arrayLock)
{
    if (BigArray.Count != 0)
        BigArray.RemoveAt(0);
}

2. 改用线程安全集合

如果场景允许,直接用.NET内置的ConcurrentQueue<T>代替普通集合。它的TryDequeue方法本身就是线程安全的,能直接避免手动加锁的麻烦,还能保证元素只被处理一次:

// 替换原来的BigArray
private ConcurrentQueue<byte[]> _dataQueue = new ConcurrentQueue<byte[]>();

// 线程里的处理逻辑可改为:
if (_dataQueue.TryDequeue(out byte[] data))
{
    Processing(data);
    // 其他处理逻辑
}

3. 重新梳理线程同步逻辑

当前依赖线程索引的AutoResetEvent同步逻辑有问题,没正确协调集合修改的节奏。如果必须保留按索引处理的逻辑,得确保同一时间只有一个线程能执行删除操作,并且删除后通知其他线程重新检查集合状态。比如用Monitor.Wait和Monitor.Pulse来替代AutoResetEvent,更灵活地控制线程等待集合有足够元素的场景。

额外提醒

  • 永久循环的线程一定要加退出条件,比如用CancellationToken,否则程序退出时线程可能无法正常终止。
  • 当前逻辑里,线程按索引取元素的方式可能导致重复处理或处理已被删除的元素,加锁或改用线程安全集合后要重新验证这部分逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 09:22:06