Reputation: 21
BackGround
I have created a simple program which "races" threads. This is not complete, but it's a test bed for learning new things.
Right now, I'm at the "learning how to create threads" stage.
The program looks like this:
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10, i++)
{
Thread racer = new Thread(() => GetRacer(i));
racer.Start();
{
Console.ReadLine();
}
public static Racer GetRacer(int designation)
{
return new Racer(Convert.ToString(designation));
}
}
public class Racer
{
public Racer(string name)
{
Random myRand = new Random(1234);
Thread.Sleep(myRand.Next(1000, 2000));
Console.WriteLine(name);
}
{
Problem
The specific problem that I am having is that multiple Racers have the same name, but the value of i
changes before the new Racer is created. Why is this so?
Upvotes: 0
Views: 297
Reputation: 21
After inspecting the IL (which was my first time doing so, I'll admit), I found that the problem lies in the way in which Lambdas are implemented. As a disclaimer, I don't fully understand what's happening here, but I think I understand enough to know what the issue is.
If we look at the IL, we can see that the compiler creates a new object called <>c__displayclass0_0
. This class is the way that the lambda is implemented.
It creates a field .field public int32 i
, which corresponds to the input of the lambda, and a method which corresponds to the lambda itself.
The Main
method only has one instance of the <>c__displayclass0_0
. Whenever the for loop changes the value of i
, it also updates the value of <>c__displayclass0_0.field public int32 i
.
It then creates and starts the new thread, passing it a reference to the one instance of <>c__displayclass0_0
. Then, because that iteration of the for loop is complete, it starts the for loop again. As such, if the value of <>c__displayclass0_0.field public int32 i
happens to change before the new thread gets execution time, then the new value of i
will be used.
This has the interesting side effect that sometimes a thread will display the value 10, because the for loop increments i before checking to see if it's below 10.
Upvotes: 1
Reputation: 521
The answer lies in comment from @Sami, you can solve it by using another local variable.
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
int copy = i;
Thread racer = new Thread(() => GetRacer(copy));
racer.Start();
}
Console.ReadLine();
}
public static Racer GetRacer(int designation)
{
return new Racer(Convert.ToString(designation));
}
}
public class Racer
{
public Racer(string name)
{
Random myRand = new Random(1234);
Thread.Sleep(myRand.Next(1000, 2000));
Console.WriteLine(name);
}
}
More explanations can be found in similar post. Multithreading and closures in .NET
Upvotes: 1