Imaging a small C# winform app with just one ListBox and the following code:
private void Form1_Load(object sender, EventArgs e) { for (int i = 0; i < 5; i++) { Thread t = new Thread(() => AddToListbox(i)); t.Start(); } } private void AddToListbox(int i) { if (this.InvokeRequired) this.Invoke(new Action<int>(AddToListbox), i); else this.listBox1.Items.Add(i); }
A simple loop iterating over the numbers from 0 to 4 and adding these values asynchronous to a ListBox. What do you expect? I expected the numbers from 0 to 4 shown in the ListBox but in any random order since I do not control the threads in any way.
I didn’t expect any number to appear multiple times and I’m totally surprised to see the number 5!
But ReSharper gave me a hint that I often saw but never understood:
So what’s going on?
I use the syntax of an Lambda expression instead of a regular function call (e.g. using the ThreadStart class and a delegate). This Lambda expression is not evaluated until the thread uses it. And by this time, the loop can be in its next iteration. If the loop is already finished i
will be 5.
That is exactly what R# tries to tell me: “Hey, you are accessing here a variable but change it later on. Maybe that is not a good idea.”. – It isn’t.
The solution
Just make a copy of i
before passing it into the expression. This copy must be a private copy that will not be changed later. The easiest way to do so is declaring a variable inside the body of the loop. In every iteration of the loop a new integer will be created on the stack and the Lambda expression will access this one.
for (int i = 0; i < 5; i++) { int copy = i; Thread t = new Thread(() => AddToListbox(copy)); t.Start(); }
Recent Comments