0

I'm trying to spawn different threads for some processing. I use the for loop index for some logic inside each thread.
How can I get the different threads to print 1,2,3,4, 5 in the code below? Each time I run this, I get different numbers as output - 3,3,3,4,6,6 & 2,2,3,5,5,6 etc.
I tried using the lock object, but it stil wasn't doing it correctly. Can anyone help me achive this. I just want to make sure each thread/task gets the right index. Note that each task has been forced to run on a separate thread.

List<Task> tasks1 = new List<Task>();
for (int j = 1; j <= 5; j++)
{    
   tasks1.Add(Task.Factory.StartNew(() =>
          {
              Console.WriteLine(j);
          }
          , new CancellationToken()
          , TaskCreationOptions.LongRunning
          , TaskScheduler.Default)
                                   );
}

Task.WaitAll(tasks1.ToArray());
Console.Read();
Alex J
  • 1,547
  • 2
  • 26
  • 41

3 Answers3

4

You are 'capturing the loop variable'. The fact that j is used inside the lambda means the compiler will treat it differently (in essence, it will be boxed) and all thread will use the same shared variable.

The short fix:

 for (int j = 1; j <= 5; j++)
 {
     int jCopy = j;

     tasks1.Add(Task.Factory.StartNew(() =>
      {
          Console.WriteLine(jCopy);
      }, new CancellationToken(), TaskCreationOptions.LongRunning, TaskScheduler.Default)

     );
 }
H H
  • 263,252
  • 30
  • 330
  • 514
3

Just to complete the other answers. In C# 5.0 (.NET 4.5) there was a breaking change in relation to closing over foreach loop variable, but not to closure over for loop variable.

See details (and preface Update note) in Eric Lippert. Closing over the loop variable considered harmful and his Closing over the loop variable, part two

Note that this issue is independent on multithreading or TPL (Task Parallel Library) usage.

The other answers and comments mentioned that it was discussed before but there was no linking to any of the previous answers. Here are some, for the sake of inter-linking:

Community
  • 1
  • 1
2

Answered millions of times. it is related with closure. Change your code as below

 for (int j = 1; j <= 5; j++)
 {
     int temp = j;

     tasks1.Add(Task.Factory.StartNew(() =>
      {
          Console.WriteLine(temp);
      }, new CancellationToken(), TaskCreationOptions.LongRunning, TaskScheduler.Default)

     );
 }

I would recomment to read this: Loop variables and Closures

I4V
  • 34,891
  • 6
  • 67
  • 79
  • +1 for "Answered millions of times". Stack Overflow has become a garbage dump for such questions. – usr Jun 04 '13 at 21:24
  • apprently, it is such a source for confusion that they changed it in 3.0, http://www.mindscapehq.com/blog/index.php/2012/03/18/what-else-is-new-in-c-5/. Sorry I asked the question. – Alex J Jun 04 '13 at 21:33
  • @AlexJ I hope you mean `foreach` not `for` (and in c# 5.0 not 3.0) – I4V Jun 04 '13 at 21:35