Does tasks.ToList() create a list containing newly copied tasks or the list refers to the same tasks?

ぐ巨炮叔叔 提交于 2020-01-04 13:46:20

问题


Assume we have an array of tasks (called 'tasks') and then convert it to a list (called 'temp') by saying:

var temp = tasks.ToList();

What happens to those running tasks to which the array elements point? Do we have two sets of tasks running separately (one in the 'tasks' and the other in 'temp')? Or they point to the same tasks?

The following code (taken from the book Exam Ref 70-483 ) is relevant to what I'm saying (the last three lines):

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();
}

UPDATE: I know the purpose of the last three lines but do not know why it work.


回答1:


What happens [when we call ToList on a sequence of tasks]? Do we have two sets of tasks running separately? Or they point to the same tasks?

This is actually a good question, and it depends on the source sequence on which you call ToList. If temp is a collection of running tasks (as in your case), then ToList simply creates a copy of their references, meaning your new list would point to the same set of tasks.

However, if temp constitutes a sequence of tasks that have yet to be instantiated (due to deferred execution), then you would get a new set of tasks each time you call ToList. Here is a simple example (using ToArray, which has the same effect as 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);

If you run the code above, the final result will be 20, meaning that two batches of tasks were run (one for each ToArray call). However, if you uncomment the line that enables eager execution, the final result will become 10, indicating that the subsequent ToArray calls only copy the references, rather than spawn new tasks.




回答2:


ToList creates a new list containing the IEnumerable's items. It does not create a deep copy of those items, it only copies the references. So there is only a single set of tasks.

Those lines are probably there so the developer could use RemoveAt to easily remove an item using an index (the index returned from Task.WaitAny)



来源:https://stackoverflow.com/questions/24175033/does-tasks-tolist-create-a-list-containing-newly-copied-tasks-or-the-list-refe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!