使用Queue计算9个浮点值平均值时偶发InvalidOperationException异常
解决Queue计算平均值时偶尔触发的InvalidOperationException问题
嘿,这个异常我之前做游戏开发处理玩家运动数据时也碰到过!咱们先把问题拆明白:你看到的InvalidOperationException来自Linq的Average()方法,核心原因要么是计算平均值的时候Queue是空的,要么是多线程下Queue被并发修改导致枚举出问题了。
可能的原因和对应的解决办法
1. 空队列直接调用了Average()
Linq的Average()方法如果拿到空序列,直接就会报错——毕竟没法计算0个元素的平均值嘛。如果你的Queue偶尔会被清空(比如玩家状态重置的时候),刚好这时候触发平均值计算,就会出这个问题。
修复方案:
计算前先判断队列里有没有元素,给个默认值兜底:
float avgVelocity = myQueue.Any() ? myQueue.Average() : 0f; // 0f可以换成你需要的默认值,比如前一次的平均值
2. 多线程并发访问Queue引发的问题
Queue<T>本身不是线程安全的!如果你的OnPersonUpdated事件可能在多个线程触发(比如Unity里的网络回调、异步事件),同时另一个线程在计算平均值,那枚举Queue元素的过程中,Queue被其他线程修改(比如入队、出队),就会直接抛出这个异常。
两种靠谱的解决思路:
- 换用线程安全的
ConcurrentQueue<T>:这玩意儿是专门给多线程场景设计的,枚举和增删操作都做了同步,不会因为并发修改出错。用法和普通Queue差不多,只是部分方法名有变化(比如取元素用TryDequeue)。 - 给普通Queue加锁:用一个锁对象把所有对Queue的读写操作都包起来,确保同一时间只有一个线程能操作它:
// 先定义一个锁对象,要和Queue一起初始化 private readonly object _queueLock = new object(); private Queue<float> _velocityQueue = new Queue<float>(9); // 初始化容量设为9更高效 // 在OnPersonUpdated里入队的时候加锁 private bool OnPersonUpdated(IEvent evt) { var castEvent = evt as Event_Update; if (castEvent == null || !peopleDict.ContainsKey(castEvent.id)) return false; float xVel = castEvent.xVel; // 假设你这里拿到了速度值 lock(_queueLock) { _velocityQueue.Enqueue(xVel); // 保持队列最多9个元素 if(_velocityQueue.Count > 9) _velocityQueue.Dequeue(); } // 其他逻辑... return true; } // 计算平均值的时候也加锁 private float GetAverageVelocity() { lock(_queueLock) { return _velocityQueue.Any() ? _velocityQueue.Average() : 0f; } }
结合你的代码场景分析
从你给出的OnPersonUpdated代码片段来看,你应该是在事件回调里往Queue塞速度数据。如果这个事件是异步触发的(比如网络同步过来的玩家数据),那多线程并发访问的概率极高,优先用ConcurrentQueue或者加锁的方式解决,基本就能搞定这个偶尔出现的异常了。
内容的提问来源于stack exchange,提问作者Bachalo
相关产品推荐
相关产品推荐

