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?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
selams
  • 105
  • 1
  • 3

1 Answers1

8

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();
}
Ňuf
  • 6,027
  • 2
  • 23
  • 26
  • Perfect answer and well explained ! – Rainer Schaack Nov 18 '17 at 13:27
  • 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. – Papa Smurf Dec 01 '18 at 14:09