2

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.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
Kamran Bigdely
  • 7,946
  • 18
  • 66
  • 86

2 Answers2

9

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.

Douglas
  • 53,759
  • 13
  • 140
  • 188
3

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)

i3arnon
  • 113,022
  • 33
  • 324
  • 344