0

I experienced a problem today I cannot explain. I have a task which invokes a Method, but the integer parameter doesn't seem to be treathed like a value type.

I could reproduce it in a simple setting:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Start();
        }

        private static void Start()
        {
            int numberThreads = 3;

            for (int i = 0; i < numberThreads; i++)
            {
                if (i == 3)
                {
                    // Never gets hit
                    System.Diagnostics.Debugger.Break();
                }
                Task.Run(() => DoWork(i));
            }
        }

        private static void DoWork(int index)
        {
            if (index == 3)
            {
                // index = 3
                System.Diagnostics.Debugger.Break();
            }
        }
    }
}

The (i == 3) in Start() never validates true, the (index == 3) in DoWork() is always true. How is this possible?

FrankyHollywood
  • 1,497
  • 19
  • 18
  • 3
    Do you want to know why your `if (i == 3)` doesn't get hit? It's because your for loops starts at 0. Change it to `for (int i = 1; i< numberThreads; i++)` and it should hit your if statement. –  Nov 29 '16 at 10:21
  • Won't help -- you must also change the condition from `<` to `<= `. – Petter Hesselberg Nov 29 '16 at 10:21
  • @James, yes, that is the strange thing. I would not expect that could ever happen – FrankyHollywood Nov 29 '16 at 10:22
  • 6
    In the delegate passed to `Task.Run` you are closing over the variable `i`, not the current value in the loop. You need to copy the value e.g. `int j = i; Task.Run(() => DoWork(j))`. – Lee Nov 29 '16 at 10:23
  • See http://stackoverflow.com/questions/227820/why-is-it-bad-to-use-an-iteration-variable-in-a-lambda-expression – nvoigt Nov 29 '16 at 10:26

1 Answers1

4

Thats happens because you taking variable i here Task.Run(() => DoWork(i));, but Task will not start immediately and FOR loop can change i before that Task runs. Thats why you can handle i==3 inside task.

for (int i = 0; i < numberThreads; i++)
{
    if (i == 3)
    {
         // Never gets hit
         System.Diagnostics.Debugger.Break();
    }
    Task.Run(() => DoWork(i)); <= action will executed after we exited from for loop
}

For more information pls see here Captured variable in a loop in C#

And ofcource I advicing to read this article http://csharpindepth.com/Articles/Chapter5/Closures.aspx

Community
  • 1
  • 1
tym32167
  • 4,741
  • 2
  • 28
  • 32