|
ms
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Anonymous Methods As ThreadStartsto a thread by using an anonymous method as your ThreadStart. It seemed pretty slick and even worked when I tried it. However, with some more playing around, I got some really strange results. Using the code below, sometimes my output is 2 and 3. Sometimes, it's 3 and 3. However, it's never been 1 and 2 which is what I would expect. Can anybody set me straight on why I'm getting the unexpected results? My suspicion is that somehow my integer is getting passed by reference rather than value, but I'd like someone else to corroborate that before I go on believing it. (Note: I know about ParameterizedThreadStart. I'm just trying to use this example to understand anonymous methods a little better.) namespace AnonymousConfusion { class Program { static void Main(string[] args) { int i = 1; ThreadStart ts1 = delegate { WorkThread(i); }; Thread t1 = new Thread(ts1); // i should be 1 when i'm starting my thread, right? t1.Start(); i++; ThreadStart ts2 = delegate { WorkThread(i); }; Thread t2 = new Thread(ts2); // i should 2 when i'm starting my thread, right? t2.Start(); i++; t1.Join(); t2.Join(); // why am i getting 2 and 3 or sometimes 3 and 3 for output? Console.WriteLine("Done"); Console.ReadLine(); } static void WorkThread(int i) { Console.WriteLine(i.ToString()); } } } This has nothing to do with Anonymous methods, other than the fact that you
used an anonymous method to create the threads. The nature of threads is that they run independently of each other and of the parent thread. Therefore, the time it takes for each thread to process is in no way related to the order in which they are created. -- Show quoteHide quoteHTH, Kevin Spencer Microsoft MVP ..Net Developer Presuming that God is "only an idea" - Ideas exist. Therefore, God exists. <d225***@yahoo.com> wrote in message news:1141998999.464222.179090@i39g2000cwa.googlegroups.com... >I read an article that had a really elegant solution to pass parameters > to a thread by using an anonymous method as your ThreadStart. It > seemed pretty slick and even worked when I tried it. However, with > some more playing around, I got some really strange results. Using the > code below, sometimes my output is 2 and 3. Sometimes, it's 3 and 3. > However, it's never been 1 and 2 which is what I would expect. Can > anybody set me straight on why I'm getting the unexpected results? My > suspicion is that somehow my integer is getting passed by reference > rather than value, but I'd like someone else to corroborate that before > I go on believing it. (Note: I know about ParameterizedThreadStart. > I'm just trying to use this example to understand anonymous methods a > little better.) > > namespace AnonymousConfusion > { > class Program > { > static void Main(string[] args) > { > int i = 1; > > ThreadStart ts1 = delegate { WorkThread(i); }; > Thread t1 = new Thread(ts1); > // i should be 1 when i'm starting my thread, right? > t1.Start(); > > i++; > > ThreadStart ts2 = delegate { WorkThread(i); }; > Thread t2 = new Thread(ts2); > // i should 2 when i'm starting my thread, right? > t2.Start(); > > i++; > > t1.Join(); > t2.Join(); > > // why am i getting 2 and 3 or sometimes 3 and 3 for > output? > Console.WriteLine("Done"); > Console.ReadLine(); > } > > static void WorkThread(int i) > { > Console.WriteLine(i.ToString()); > } > } > } > This makes perfect sense; the "i" in the anonymous delegate (I'm not on
about WorkThread here, just the bit in the braces) is the same i - and it all depends on timing: [thread 1] i=1 [thread 1] go to start a thread [thread 1] increment i (now = 2) [thread 2] catches up, call WorkThread with i, currently 2 [thread 1] go to start a thread [thread 1] increment i (now = 3) [thread 3] catches up, call WorkThread with i, currently 3 or [thread 1] i=1 [thread 1] go to start a thread [thread 1] increment i (now = 2) [thread 1] go to start a thread [thread 1] increment i (now = 3) [thread 2] catches up, call WorkThread with i, currently 3 [thread 3] catches up, call WorkThread with i, currently 3 In a way, you have been lucky; this type of usage could also lead to completely phantom reads of i, particularly if i is of a larger data-type (long etc); for this (latter) reason you should *always* sync access to variables when using multiple threads. But the main behaviour makes perfect sense Marc <d225***@yahoo.com> wrote in message Show quoteHide quote news:1141998999.464222.179090@i39g2000cwa.googlegroups.com... >I read an article that had a really elegant solution to pass parameters > to a thread by using an anonymous method as your ThreadStart. It > seemed pretty slick and even worked when I tried it. However, with > some more playing around, I got some really strange results. Using the > code below, sometimes my output is 2 and 3. Sometimes, it's 3 and 3. > However, it's never been 1 and 2 which is what I would expect. Can > anybody set me straight on why I'm getting the unexpected results? My > suspicion is that somehow my integer is getting passed by reference > rather than value, but I'd like someone else to corroborate that before > I go on believing it. (Note: I know about ParameterizedThreadStart. > I'm just trying to use this example to understand anonymous methods a > little better.) > > namespace AnonymousConfusion > { > class Program > { > static void Main(string[] args) > { > int i = 1; > > ThreadStart ts1 = delegate { WorkThread(i); }; > Thread t1 = new Thread(ts1); > // i should be 1 when i'm starting my thread, right? > t1.Start(); > > i++; > > ThreadStart ts2 = delegate { WorkThread(i); }; > Thread t2 = new Thread(ts2); > // i should 2 when i'm starting my thread, right? > t2.Start(); > > i++; > > t1.Join(); > t2.Join(); > > // why am i getting 2 and 3 or sometimes 3 and 3 for > output? > Console.WriteLine("Done"); > Console.ReadLine(); > } > > static void WorkThread(int i) > { > Console.WriteLine(i.ToString()); > } > } > } > > I read an article that had a really elegant solution to pass If you take a look using Reflector or some other disassembly tool you'll > parameters to a thread by using an anonymous method as your > ThreadStart. It seemed pretty slick and even worked when I tried it. > However, with some more playing around, I got some really strange > results. Using the code below, sometimes my output is 2 and 3. > Sometimes, it's 3 and 3. However, it's never been 1 and 2 which is notice that a method that declares an anonymous method and has local variables that is used in that method will have some "magic" code in it. Basically what happens is that a class to hold the method is defined, and the local variables in your outer method is stored in that class. When the method starts, an object is constructed from that class and used internally. As such, your thread(s) and outer method shares the same variables with each other. This means that you get all the features of sharing variables easily and all the headache when doing so with threads. Let me show you an actual example: public void Test() { String s = String.Empty; System.Threading.ThreadStart ts = delegate { s = "Set in delegate"; }; ts(); System.Diagnostics.Debug.WriteLine(s); } The decompiled version of this looks like this: public void Test() { MainForm.<>c__DisplayClass1 class1 = new MainForm.<>c__DisplayClass1(); class1.s = string.Empty; ThreadStart start1 = new ThreadStart(class1.<Test>b__0); start1(); Debug.WriteLine(class1.s); } and then: [CompilerGenerated] private sealed class <>c__DisplayClass1 { // Methods public <>c__DisplayClass1() { } public void <Test>b__0() { this.s = "Set in delegate"; } // Fields public string s; } So, as Kevin noted, the threads all work on the same variable and might not even get scheduled some time before you increment the variable and start the next thread, and that's why you get those results. -- Lasse Vågsæther Karlsen http://usinglvkblog.blogspot.com/ mailto:la***@vkarlsen.no PGP KeyID: 0x2A42A1C2 d225***@yahoo.com wrote:
> I read an article that had a really elegant solution to pass parameters It's not that it's being "passed" so much as that it's being shared> to a thread by using an anonymous method as your ThreadStart. It > seemed pretty slick and even worked when I tried it. However, with > some more playing around, I got some really strange results. Using the > code below, sometimes my output is 2 and 3. Sometimes, it's 3 and 3. > However, it's never been 1 and 2 which is what I would expect. Can > anybody set me straight on why I'm getting the unexpected results? My > suspicion is that somehow my integer is getting passed by reference > rather than value, but I'd like someone else to corroborate that before > I go on believing it. between the delegates. It's a captured variable - not really a local variable any more. See http://www.pobox.com/~skeet/csharp/csharp2/delegates.html for more information and a really scary example... Jon <d225***@yahoo.com> wrote in message
Show quoteHide quote news:1141998999.464222.179090@i39g2000cwa.googlegroups.com... Before your thread gets actually a chance to run and pick up the value of i, |I read an article that had a really elegant solution to pass parameters | to a thread by using an anonymous method as your ThreadStart. It | seemed pretty slick and even worked when I tried it. However, with | some more playing around, I got some really strange results. Using the | code below, sometimes my output is 2 and 3. Sometimes, it's 3 and 3. | However, it's never been 1 and 2 which is what I would expect. Can | anybody set me straight on why I'm getting the unexpected results? My | suspicion is that somehow my integer is getting passed by reference | rather than value, but I'd like someone else to corroborate that before | I go on believing it. (Note: I know about ParameterizedThreadStart. | I'm just trying to use this example to understand anonymous methods a | little better.) | | namespace AnonymousConfusion | { | class Program | { | static void Main(string[] args) | { | int i = 1; | | ThreadStart ts1 = delegate { WorkThread(i); }; | Thread t1 = new Thread(ts1); | // i should be 1 when i'm starting my thread, right? | t1.Start(); | | i++; | | ThreadStart ts2 = delegate { WorkThread(i); }; | Thread t2 = new Thread(ts2); | // i should 2 when i'm starting my thread, right? | t2.Start(); | | i++; | | t1.Join(); | t2.Join(); | | // why am i getting 2 and 3 or sometimes 3 and 3 for | output? | Console.WriteLine("Done"); | Console.ReadLine(); | } | | static void WorkThread(int i) | { | Console.WriteLine(i.ToString()); | } | } | } | your main thread will have incremented the value once maybe twice. Willy. To get expected results, you could pass i instead of using local var capture.
Note that t2 could still complete before t1, but they should still have the expected i. private void button3_Click(object sender, EventArgs e) { int i = 1; Thread t1 = new Thread(WorkThread); t1.Name = "t1"; t1.Start(i); i++; Thread t2 = new Thread(WorkThread); t2.Name = "t2"; t2.Start(i); i++; t1.Join(); t2.Join(); } static void WorkThread(object i) { Console.WriteLine("Thread:{0} Value:{1}", Thread.CurrentThread.Name, (int)i); } -- William Stacey [MVP]
Other interesting topics
newbie: GetType or typeof?
string dictionary and memory issue. How to Anonymous IP address in C# Sockets, to be continued... What is wrong with this Producer-Consumer sample? file uploading in windows application in .net Desing problem C++ class in DLL to be imported to C# Regular Expresions How select from 2 ADO.NET DataTables? |
|||||||||||||||||||||||