当我真的不确定原因时,我得到ArgumentOutOfRangeException
。
Task[] downloadTasks = new Task[music.Count];
for (int i = 0; i < music.Count; i++)
downloadTasks[i] = Task.Factory.StartNew(() => DownloadAudio(music[i], lstQueue.Items[i]));
Task.Factory.ContinueWhenAll(downloadTasks, (tasks) =>
{
MessageBox.Show("All the downloads have completed!",
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
});
for loop
在i = 1
运行时发生错误,我不确定为什么当我对music.Count = 1
表示肯定时会这样做。
我总是尝试将此方法作为for loop
的替代方法,并获得相同的异常:
int index = 0;
foreach (MusicFile song in music)
{
downloadTasks[index] = Task.Factory.StartNew(() => DownloadAudio(song, lstQueue.Items[index]));
index++;
}
上述代码中是否有可能导致此问题的内容?
我也不确定这是否相关,但是当我可以使用线程完成同样的事情而没有任何异常。只有当我尝试执行任务时,才会出现此异常。
答案 0 :(得分:4)
这是因为您传递StartNew
一个Lambda Expression,它会隐式捕获您的i
变量。此效果称为Closure。
为了获得正确的行为,您必须制作索引的本地副本:
for (int i = 0; i < music.Count; i++)
{
var currentIndex = i;
downloadTasks[i] = Task.Factory.StartNew(() =>
DownloadAudio(music[currentIndex],
lstQueue.Items[currentIndex]));
}
答案 1 :(得分:1)
在这两种情况下,您在第一个示例中为closing over the loop variable i
,或在第二个示例中为您手动分配的index
。
正在发生的是i / index
的最终值在循环完成后使用,即i++
增加超过迭代数组的大小。 (见also here)
根据@Yuval使用附加变量捕获循环内部的i值,或者查看将两个集合耦合在一起的方法,这样您就不需要迭代music
和{{ 1}}独立地,例如在这里,我们将两个集合预先组合成一个新的匿名类:
lstQueue
答案 2 :(得分:0)
闭包是你的问题,变量i由lambada表达式引用,因此它可以访问i并始终直接从内存中读取它的值。
您可以创建一个创建任务处理程序的工厂函数。您可以按照以下想法解决问题。
private Action CreateTaskHandler(int arg1)
{
return () => DownloadAudio(music[arg1], lstQueue.Items[arg1])
}
Task[] downloadTasks = new Task[music.Count];
for (int i = 0; i < music.Count; i++)
downloadTasks[i] = Task.Factory.StartNew(CreateTaskHandler(i));
}