Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why c# doesn't preserve the context for an anonymous delegate calls?

I have the following method:

static Random rr = new Random();
static void DoAction(Action a)
{
    ThreadPool.QueueUserWorkItem(par =>
    {
        Thread.Sleep(rr.Next(200));
        a.Invoke();
    });
}

now I call this in a for loop like this:

for (int i = 0; i < 10; i++)
{
    var x = i;

    DoAction(() =>
    {
        Console.WriteLine(i); // scenario 1
        //Console.WriteLine(x); // scenario 2
    });
}

in scenario 1 the output is: 10 10 10 10 ... 10
in scenario 2 the output is: 2 6 5 8 4 ... 0 (random permutation of 0 to 9)

How do you explain this? Is c# not supposed to preserve variables (here i) for the anonymous delegate call?

like image 386
Mo Valipour Avatar asked Jan 13 '12 16:01

Mo Valipour


3 Answers

The problem here is that there is one i variable and ten instances / copies of x. Each lambda gets a reference to the single variable i and one of the instances of x. Every x is only written to once and hence each lambda sees the one value which was written to the value it references.

The variable i is written to until it reaches 10. None of the lambdas run until the loop completes so they all see the final value of i which is 10

I find this example is a bit clearer if you rewrite it as follows

int i = 0;  // Single i for every iteration of the loop
while (i < 10) { 
  int x = i;  // New x for every iteration of the loop 
  DoAction(() => {
    Console.WriteLine(i);
    Console.WriteLine(x);
  });
  i++;
};
like image 145
JaredPar Avatar answered Nov 08 '22 08:11

JaredPar


DoAction spawns the thread, and returns right away. By the time the thread awakens from its random sleep, the loop will be finished, and the value of i will have advanced all the way to 10. The value of x, on the other hand, is captured and frozen before the call, so you will get all values from 0 to 9 in a random order, depending on how long each thread gets to sleep based on your random number generator.

like image 23
Sergey Kalinichenko Avatar answered Nov 08 '22 09:11

Sergey Kalinichenko


I think you'll get the same result with java or any Object oriented Language (not sure but here it seems logical).

The scope of i is for the whole loop and the scope of x is for each occurrence.

Resharper helps you top spot this kind of problem.

like image 26
remi bourgarel Avatar answered Nov 08 '22 09:11

remi bourgarel