5

I am trying to fully understand how creating separate threads that call the same method of a class instance can effect local variables in the method.

For example I have a class with a single method (Divide)

public class Maths
{
    public int Num1;
    public int Num2;

    public void Divide()
    {
        for (long i = 0; i < 100000; i++)
        {
            Num1 = 2;
            Num2 = 2;
            int result = Num1 / Num2;
            Num1 = 0;
            Num2 = 0;
        }
    }
}

Two threads are instantiated and the divide method called as follows:

    static void Main(string[] args)
    {
        Maths m = new Maths();

        Task t1 = new Task(() => m.Divide());
        Task t2 = new Task(() => m.Divide());

        List<Task> tl = new List<Task> { t1, t2 };
        Parallel.ForEach(tl, task => task.Start());

        Console.ReadLine();
    }

}

Sometimes this code runs ok. But other times it throws a dividebyzero error on the line:

int result = Num1 / Num2;

My assumption is that one of the threads is resetting the Num1 and Num2 to zero just before the other thread calls the Num1 / Num2. Therefore causing a divide by zero exception.

This would make sense and I should use a lock but I don't understand how these local variables Num1 and Num2 are shared between the threads because my understanding was that local variables are not shared between threads?

1
  • Thanks, that makes sense now. Commented Nov 18, 2017 at 13:33

1 Answer 1

10

You are right that local variables are not shared between threads (typically, each time method is called, new set of it's local variables is allocated on the stack of executing thread, so local variables of each method call are completely separate and modification of one of them have no effect on the others).

But unfortunatelly Num1 and Num2 are not local variables, but fields. Fields of the same instance of a class are shared between threads.

You would need to declate them like this to make them local variables:

public class Maths
{
    public void Divide()
    {
        int Num1;
        int Num2;

        for (long i = 0; i < 100000; i++)
        {
            Num1 = 2;
            Num2 = 2;
            int result = Num1 / Num2;
            Num1 = 0;
            Num2 = 0;
        }
    }
}

Alternatively, you coud create separate instance of Maths class for each thread, so each thread would use fields Num1 and Num2 of different instances of Maths class:

static void Main(string[] args)
{
    Maths m1 = new Maths();
    Maths m2 = new Maths();

    Task t1 = new Task(() => m1.Divide());
    Task t2 = new Task(() => m2.Divide());

    List<Task> tl = new List<Task> { t1, t2 };
    Parallel.ForEach(tl, task => task.Start());

    Console.ReadLine();
}
Sign up to request clarification or add additional context in comments.

2 Comments

Perfect answer and well explained !
Actually, the variable m in Main is a local variable. It is a reference to a heap object, and this reference is not exactly shared, but equal on the stacks of both threads. So it makes sometimes sense to lock a local variable.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.