ArgumentOutOfRangeException Using Tasks

本文关键字:Tasks Using ArgumentOutOfRangeException | 更新日期: 2023-09-27 18:35:31

当我真的不确定为什么时,我得到了一个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++;
}

上面的代码中是否有任何可能导致这种情况的内容?

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

ArgumentOutOfRangeException Using Tasks

发生这种情况是因为您StartNew传递 Lambda 表达式,该表达式隐式捕获您的i变量。这种效果称为闭包。

为了获得正确的行为,您必须创建索引的本地副本:

for (int i = 0; i < music.Count; i++)
{
    var currentIndex = i;
    downloadTasks[i] = Task.Factory.StartNew(() => 
                                             DownloadAudio(music[currentIndex],
                                             lstQueue.Items[currentIndex]));
}

在这两种情况下,您都在关闭第一个示例中的循环变量i,或者在第二个示例中关闭手动分配的index

正在发生的事情是,i / index的最终值在循环完成后使用,即当i++已经增加到超过迭代数组的大小时。(另见此处)

要么根据@Yuval使用附加变量在循环中捕获 i 的值,要么查看将两个集合耦合在一起的方法,这样您就不需要独立迭代musiclstQueue,例如,在这里我们将两个集合预先组合成一个新的匿名类:

var musicQueueTuples = music.Zip(lstQueue, (m, q) => new {Music = m, QueueItem = q})
    .ToList();
// Which now allows us to use LINQ to project the tasks:
var downloadTasks = musicQueueTuples.Select(
       mqt => Task.Factory.StartNew(
         () => DownloadAudio(mqt.Music, mqt.QueueItem))).ToArray();
Task.Factory.ContinueWhenAll(downloadTasks, (tasks) => ...

包是你的问题,其中变量 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));
}