0

I have this code, which works as I wanted but I don't understand exactly why. Thinking about a stack in C, C++, I'd guess that the p variable will be on the stack on each call, and then erased when the method returns. How does the closure of the thread captures it and more over, captures the correct value every time? The output is what I wanted - files are "_a", "_b", "_c".

public enum enumTest
    {
        a = 1,
        b =2,
        c=3
    }     
 private void Form1_Load(object sender, EventArgs e)
    {

        callme(enumTest.a);
        callme(enumTest.b);
        callme(enumTest.c);
    }

    private void callme(enumTest p)
    {
        Thread t = new Thread(() =>
            {
                Thread.Sleep(2000);
                Guid guid = Guid.NewGuid();
                File.WriteAllText(guid.ToString() + "_" + p.ToString(), "");
            });
        t.Start();
    }
kobigurk
  • 731
  • 5
  • 14

3 Answers3

1

It's not about closures, here is no any value capturing.

What happening here is that your p parameter is copied by value into the thread's function. Everytime you pass to a function a new value of p is copied to a function.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • 2
    Actually `p` is closed-over here but that is irrelevant with the given code. – H H Jan 31 '13 at 09:23
  • I see. So if I try to see what's going on behind the scenes it's something like this : when the thread's anonymous function is declared, a copy of the variable is stored in the thread function. Henk, Why is the closure irrelevant though? – kobigurk Jan 31 '13 at 09:32
  • Tigran, I appreciate your answer a lot, but Richard's helped me better. – kobigurk Jan 31 '13 at 10:37
1

Lambdas are just glorified anonymous delegates

Rick's article describes how the compiler generates a class that handles the enumTest p value and delegate.

Also good info at Where does anonymous function body variables saved ?

Basically the compiler creates a new instance of the "closure class" with local variables that must be passed to lambda. This is why you output is correct.

UPDATE

In the case of:

for (int i=0; i<10; i++) 
{
    var t = new Thread(() => { Console.WriteLine(i); }); 
    t.Start(); 
}

The variable i is shared between the for and the lambda. Each thread is accessing the same i. And since the for loop tends to finsih before any thread runs, all you see is '10'.

See http://msdn.microsoft.com/en-us/library/0yw3tz5k(v=vs.80).aspx

Community
  • 1
  • 1
Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
  • That's a really nice article! But now it led me to another confusion. If this is the class generated for a lambda, why doesn't it work in the following code as well? Here the print happens to the last i, and not a copy of i every time : `for (int i =0; i<10; i++) { Thread t = new Thread( () => { Console.WriteLine(i); }); t.Start(); }` – kobigurk Jan 31 '13 at 09:39
  • Thanks, Richard. So if I understand correctly, the for loop variable `i` is in the same scope as each of the anonymous methods, where in my first example there are 3 different scopes, with each its own copy of `p`. – kobigurk Jan 31 '13 at 10:25
  • Yes, thats my understanding. Its all about variable scope. – Richard Schneider Jan 31 '13 at 10:28
  • Another great article about it : http://www.codeproject.com/Articles/15624/Inside-C-2-0-Anonymous-Methods#6 – kobigurk Jan 31 '13 at 10:38
1

How does the closure of the thread captures it and more over, captures the correct value every time?

That is compiler magic. Simply because the p parameter is being used by the lambda the compiler treats it differently. p is not placed on the stack but on the heap. That is why it still exists after callme() has terminated.

H H
  • 263,252
  • 30
  • 330
  • 514