为何添加防越界条件仍出现C#数组索引越界异常?
闭包捕获引发的Task索引越界问题分析与解决
问题根源
你的代码里的Task.Run使用lambda表达式捕获了idplayer变量,但闭包捕获的是变量的引用,而非循环迭代时的变量值:
- 循环的执行速度远快于后台Task的启动和执行逻辑,当Task真正开始调用
Updateplayer(players[idplayer])时,循环已经把idplayer的值递增到了超出players.Count的范围。 - 虽然
while循环条件检查了idplayer < players.Count,但这只是在循环迭代的瞬间生效,Task的异步执行不会等待循环步完成再运行,因此后续Task执行时,idplayer的值可能已经超出了合法索引范围。
解决方案
方式一:循环内创建局部变量保存当前索引值
每次迭代时把idplayer的当前值赋值给一个局部变量,让lambda捕获这个局部变量(每个迭代的局部变量是独立的,不会被后续循环修改):
Task[] lestasks = new Task[5]; int idplayer = 0; int numtask = 0; while (numtask < 5 && idplayer < players.Count) { // 保存当前迭代的索引值到局部变量 int currentId = idplayer; lestasks[numtask] = Task.Run(() => { Updateplayer(players[currentId]); }); numtask++; idplayer++; } Task.WaitAll(lestasks);
方式二:改用foreach循环(C# 5及以上版本)
C# 5及之后的foreach会为每个迭代创建独立的变量,自动避免闭包捕获的陷阱,代码更简洁:
var lestasks = new List<Task>(); int numtask = 0; foreach (var player in players) { if (numtask >= 5) break; lestasks.Add(Task.Run(() => { Updateplayer(player); })); numtask++; } Task.WaitAll(lestasks.ToArray());
额外注意
原代码中lestasks是固定长度为5的数组,如果players.Count小于5,数组中未赋值的元素会是null,调用Task.WaitAll时会抛出异常。建议改用List<Task>动态添加任务,避免空Task的问题。
内容的提问来源于stack exchange,提问作者UserNam3
相关产品推荐
相关产品推荐

