tasks. tolist()是创建一个包含新复制任务的列表,还是该列表引用相同的任务?

本文关键字:任务 列表 引用 复制 创建 tolist 包含新 一个 tasks | 更新日期: 2023-09-27 18:08:19

假设我们有一个任务数组(称为" tasks "),然后通过以下命令将其转换为列表(称为" temp "):

var temp = tasks.ToList();

数组元素指向的运行任务会发生什么?我们是否有两组单独运行的任务(一个在"tasks"中,另一个在"temp"中)?或者它们指向相同的任务?

以下代码(摘自《考试参考70-483》一书)与我所说的内容(最后三行)相关:

Task<int>[] tasks = new Task<int>[3];
tasks[0] = Task.Run(() => { Thread.Sleep(2000); return 1; });
tasks[1] = Task.Run(() => { Thread.Sleep(1000); return 2; });
tasks[2] = Task.Run(() => { Thread.Sleep(3000); return 3; });
while (tasks.Length > 0) {
    int i = Task.WaitAny(tasks);
    Task<int> completedTask = tasks[i];
    Console.WriteLine(completedTask.Result);
    var temp = tasks.ToList();
    temp.RemoveAt(i);
    tasks = temp.ToArray();
}

更新:我知道最后三行的目的,但不知道为什么它工作。

tasks. tolist()是创建一个包含新复制任务的列表,还是该列表引用相同的任务?

[当我们在一系列任务上调用ToList]时会发生什么?我们是否有两组单独运行的任务?或者它们指向相同的任务?

这实际上是一个很好的问题,它取决于你称之为ToList的源序列。如果temp是运行任务的集合(就像您的情况一样),那么ToList只是创建它们引用的副本,这意味着您的新列表将指向相同的任务集。

然而,如果temp构成了一个尚未实例化的任务序列(由于延迟执行),那么每次调用ToList时都将获得一个新的任务集。下面是一个简单的示例(使用ToArray,其效果与ToList相同):

int count = 0;
var tasks = Enumerable.Range(0, 10).Select(_ => 
    Task.Run(() => Interlocked.Increment(ref count)));
// tasks = tasks.ToArray();   // deferred vs. eager execution
Task.WaitAll(tasks.ToArray());
Task.WaitAll(tasks.ToArray());
Console.WriteLine(count);

如果运行上面的代码,最终结果将是20,这意味着运行了两批任务(每个ToArray调用一个)。但是,如果取消启用即时执行的行注释,则最终结果将变为10,这表明后续的ToArray调用只复制引用,而不生成新任务。

ToList创建一个包含IEnumerable项的新列表。它不创建这些项的深层副本,它只复制引用。所以只有一个单一的任务集。

这些行可能在那里,所以开发人员可以使用RemoveAt使用索引(从Task.WaitAny返回的索引)轻松删除项