superstewie
superstewie

Reputation: 21

Multiple threads getting same value (value type), but threads are not created until after value changes

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

Answers (2)

superstewie
superstewie

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

Muhammad Umar Farooq
Muhammad Umar Farooq

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

Related Questions