在严格的游戏循环中,我不想为GC收集任何垃圾来收集并导致游戏口吃。在许多更新循环中,我的代码与此非常相似:
List<ModifierBase> activeModsCopy;
lock (lockObj)
{
activeModsCopy = new List<ModifierBase>(_activeMods);
}
foreach (ModifierBase mod in activeModsCopy)
{
mod.Update(elapsedGameTime);
}
游戏以60 FPS运行,因此内存分析器报告了我创建的大量这些临时列表,以便在迭代时安全地防止被修改的列表。什么是快速无垃圾方法来实现相同的结果?
编辑:上面的代码在更新循环中运行。更新循环中没有并发,只有1个线程通过它,但其他线程可以添加或删除_activeMods列表中的元素。只有1个进行更新的线程可以访问activeModsCopy,因为它在更新函数中创建。
答案 0 :(得分:0)
如果您从未删除列表并重复使用该怎么办?
private ModifierBase[] _mods = new ModifierBase[2000];
private int _currentModsLength = 0;
如果您希望动态增长,可以使用List<ModifierBase>(2000)
。
和您的更新:
public void Update()
{
_currentModsLength = 0;
// copy over!!
lock(_activeMods){
_currentModsLength = _activeMods.Length;
for(var i = 0; i < _currentModsLength; i++){
_mods[i] = _activeMods[i];
}
}
for(var k = 0; k < _currentModsLength; k++)
_mods[k].Update(..);
}
如果您仍然遇到滞后,很可能不是列表复制的原因,而是您创建了许多mod并丢弃它们的事实。
或者,您可以切换列表并使用 ConcurrentBag 。基本上, ConcurrentBag 可以稍微减少线程争用。因此, ConcurrentBag 不是List,而是每个线程都有一个列表,当您以后想要组合列表时,它会将每个包锁定为微秒。
仅使用堆栈绕过GC有不安全的选项:
ModifierBase* fib = stackalloc ModifierBase[_activeMods.Length];
// do copying over