Threading and outer-variable semantics, local variables capture inanonymous methods--why?

R

RayLopez99

Why the below result? From Chap. 19 of Albahari. Makes no sense.
One or both of these threads should be outputting the AAA and CCC
strings, but they are not. see the asterisk //***

RL


/* OUTPUT

inside Print now
inside Print now
int, string are: 22, BBB...is not threaded here
The thread '<No Name>' (0xfe0) has exited with code 0 (0x0).
int, string are: 22, BBB...is not threaded here
myClassB is like BBB?: 22,BBB...is not threaded here
..............
inside Print2 now
int, string are: 333, DDD is not threaded here?! compare with above
inside Print2 now
int, string are: 333, DDD is not threaded here?! compare with above
The thread '<No Name>' (0xe3c) has exited with code 0 (0x0).


* */


private void Function001()
{
Class1 myClassA = new Class1();
myClassA.int1 = 11; myClassA.string1 =
"AAA..is..threaded!"; //*** //??!! not captured despite being before
t.Start?! why?

Thread t = new Thread(delegate() { Print(myClassA); });
t.Start(); ////*** outer-variable capture as per p. 643
Albahari *anyway!* despite being here, upstream of below?! Because
static? No!

myClassA.int1 = 22; myClassA.string1 = "BBB...is not
threaded here";
Class1 myClassB = new Class1();
myClassB = Print(myClassA);
int i = myClassB.int1; string s1 = myClassB.string1;
Debug.WriteLine("myClassB is like BBB?: " + i.ToString()
+ "," + s1); //yes, works fine

Debug.WriteLine(".............");
Class1 myClassC = new Class1();
myClassC.int1 = 33; myClassC.string1 =
"CCC..is..threaded!"; //*** //??!! not captured despite being before
t.Start?!
Thread tt = new Thread(delegate() { Print2(myClassC); });
tt.Start(); //no outer variable capture since not
static!? No. Same as before--why?
myClassC.int1 = 333; myClassC.string1 = "DDD is not
threaded here?! compare with above";
Print2(myClassC);



static Class1 Print(object messageObject)
//static version of function Print, but makes no difference if not
static
{
Class1 returnCl1 = (Class1)messageObject;
Debug.WriteLine("inside Print now");
Debug.WriteLine("int, string are: " +
returnCl1.int1.ToString() + ", " + returnCl1.string1);
return returnCl1;
}

Class1 Print2(object messageObject)
{
Class1 returnCl1 = (Class1)messageObject;
Debug.WriteLine("inside Print2 now");
Debug.WriteLine("int, string are: " +
returnCl1.int1.ToString() + ", " + returnCl1.string1);
return returnCl1;
}

public class Class1
{
public string string1;

public int int1;

public Class1()
{

}
}
 
A

Alberto Poblacion

RayLopez99 said:
Why the below result?
[...]
/* OUTPUT
inside Print now
inside Print now
int, string are: 22, BBB...is not threaded here
[...]
Class1 myClassA = new Class1();
myClassA.int1 = 11; myClassA.string1 =
"AAA..is..threaded!"; //*** //??!! not captured despite being before
t.Start?! why?

Thread t = new Thread(delegate() { Print(myClassA); });
t.Start(); ////*** outer-variable capture as per p. 643
Albahari *anyway!* despite being here, upstream of below?! Because
static? No!

myClassA.int1 = 22; myClassA.string1 = "BBB...is not
threaded here";

Note the code I have quoted. You are initialising a member in myClassA,
and then passing a reference to that member to the Print method in a new
thread. But the main thread continues runing and just below you overwrite
the member in the same instance myClassA. So when a little time later the
other thread gets to print that member, it prints the new value.
The "capture" feature does not modify this behavor. What the compiler
does when capturing a variable is that it internally creates a new class,
places the variable inside that class, and then replaces ALL references to
the variable (not only those in the anonymous method) with references to the
new member in the created class. So the preceding explanation about
overwriting the instance of the variable still holds; it is still the same
instance even if it was captured.
 
R

RayLopez99

Because the closure captures the variable, not the value of that
variable. Eric Lippert explains it in detail in his blog:

http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the...

Thank you both. This 'explains' it to me (not really explains per se,
as you would have to get to the assembly level to understand I think,
but gives me an understanding of the syntax).

Here is another example from Lippert's blog, from a user, modified for
WPF:

private void Function002()
{
/* output
* 6
* 6
* */

int i = 0;
Action foo = () => Debug.WriteLine(i.ToString());
i = 6;
Debug.WriteLine(i.ToString()); //6 as expected
foo(); //also 6, not zero
//Mneumonic: Because the closure captures the variable,
not the value of that variable. Eric Lippert explains it in detail in
his blog.


}

RL
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top