Help understand C # and multithreading

Hi I am reading the threading in c # tutorial. One of the things he mentions is this:

"The CLR assigns each thread its own memory stack, so local variables are stored separately."

And here is this example:

namespace ConsoleApplication1 { class Program { static void Main(string[] args) { for (int i = 0; i < 20; i++) { Thread t = new Thread(() => { Console.WriteLine(i); }); t.Start(); } Console.ReadLine(); } } 

}

Conclusion: 1 2 2 4 6 8 10 10 10 10 12 12 14 15 17 18 18 20 20

So I understand what is going on here:

  • The main thread starts the for loop.
  • A new stream is created and defined in such a way that it gets the value "i" and print it on the console.
  • The thread instance starts and the main thread continues to run.

Being an “i” integer, I assume that the new thread will have its own copy in the memory stack. Then print the value on the console. But, as the results show, its missing values ​​jump from 10 to 12 or from 12 to 14. So, does the new thread get a link to i? But if "i" is an integer, then the new thread will not store the new value in the memory stack instead of what seems to be a reference to i.

Also, why are there duplicate values? His seal several times 2,10, 12, 18, 20.

Thanks.

+4
source share
3 answers

This pattern is deadly wrong ... because each thread actually uses one i variable. This is captured by the lambda expression.

This is a very common problem, but it is a real shame to see it in a threading tutorial. (I hope this is not one of my articles! Please tell me where you read it.) Eric Lippert wrote about this very carefully in his blogs, “closing the loop variable considered harmful” - part 1 ; part 2 .

It is worth distinguishing between the behavior of threads and lambda expressions. Streams do have their own stacks and their own local variables, but here i is shared among all streams due to the lambda expression. This is not a local variable in the "normal" sense.

Here is an example that shows each thread that has its local variables:

 using System; using System.Threading; public class Test { static void Main() { for (int x = 0; x < 10; x++) { new Thread(Count).Start(); } } static void Count() { int threadId = Thread.CurrentThread.ManagedThreadId; Console.WriteLine("Thread {0} starting", threadId); for (int i = 0; i < 5; i++) { Console.WriteLine("{0}: {1}", threadId, i); } Console.WriteLine("Thread {0} ending", threadId); } } 

Each thread definitely prints 0..4 along with its own thread identifier. The variable i is truly local to each thread this time - there is no exchange.

+5
source

When a variable is used in a lambda expression, for example, your variable i , it rises to close (in your case, the Main method) (first hit google for "closing C #", an article by John Skeet on this issue ). And because of this, it is not a local variable and does not live on the thread stack.

+2
source

The problem is simple, since protectors take time to initialize and start, the value of i will be changed on average. In addition, it is likely that by the time other threads receive a processor cycle for processing, more than one cycle will be completed. Thus, one number is printed several times.

-1
source

Source: https://habr.com/ru/post/1339456/


All Articles