ArgumentOutOfRangeException使用任务

时间:2014-12-28 07:06:35

标签: c# .net task-parallel-library task

当我真的不确定原因时,我得到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 loopi = 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++;
}

上述代码中是否有可能导致此问题的内容?

我也不确定这是否相关,但是当我可以使用线程完成同样的事情而没有任何异常。只有当我尝试执行任务时,才会出现此异常。

3 个答案:

答案 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));
}