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();
}
更新:我知道最后三行的目的,但不知道为什么它工作。
[当我们在一系列任务上调用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
返回的索引)轻松删除项