Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple C# concurrency / multithreading

I'm probably missing something basic here, but still would appreciate your kind help in understanding this. So I have the following simple multithreaded program I wrote:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            //            List<int> outcome = new List<int>();
            Test t = new Test();

                Thread thread1 = new Thread(new ThreadStart(t.call1));
                Thread thread2 = new Thread(new ThreadStart(t.call2));
                thread1.Start();
                thread2.Start();
                Thread.Sleep(3000); //Give enough time for threads to end
                Console.Write("{0},", t.mSum);
                t.mSum = 0;
        }
    }

    class Test
    {
        public int mSum = 0;
        public void call1()
        {
            //lock (this)
            //{

            for (int i = 0; i < 100; i++)
            {
                Console.WriteLine("Hello Thread 1, mSum value: {0}", mSum);
                mSum = mSum + 1;
                Console.WriteLine("Goodbye Thread 1, mSum value: {0}", mSum);
            }
            //}
            //  Console.WriteLine(mSum);
        }
        public void call2()
        {
            for (int i = 0; i < 100 ; i++)
            {
                Console.WriteLine("Hello Thread 2, mSum value: {0}",mSum);
                mSum = mSum + 1;
                Console.WriteLine("Goodbye Thread 2, mSum value: {0}",mSum);
            }
        }
    }
}    

So I would expect this output to be nondetermenistic because context switching can occur anytime right? But when I run the program, I get the following output (only a part of the output, malformed due to my poor stackoverflow.com question posting skills):

Hello Thread 1, mSum value: 62    Goodbye Thread 1, mSum value: 63   
Hello Thread 1, mSum value: 63    Goodbye Thread 1, mSum value: 64   
Hello Thread 2, mSum value: 59    Goodbye Thread 2, mSum value: 65   
Hello Thread 2, mSum value: 65    Goodbye Thread 2, mSum value: 66

So, assuming I wrote this right, and mSum is indeed shared between the threads (looks like it...) - how can I explain line no. 3? Thread 2 reads 59, adds 1 and then we get 65!

Did I discover a new kind of math? :)

like image 343
Tal Avatar asked Aug 23 '13 12:08

Tal


People also ask

What is C simple?

C Introduction C is a general-purpose programming language created by Dennis Ritchie at the Bell Laboratories in 1972. It is a very popular language, despite being old. C is strongly associated with UNIX, as it was developed to write the UNIX operating system.

Which is simple C or C++?

We can say that C is a hands-on language and we can program it in whichever way we want. C++ consists of some high-level object-oriented programming constructs that help us to code high-level programs. Thus if we say C is easy then C++ is also easier to code.

Is C an easy language?

While C is one of the more difficult languages to learn, it's still an excellent first language pick up because almost all programming languages are implemented in it. This means that once you learn C, it'll be simple to learn more languages like C++ and C#.


2 Answers

You're not locking the shared variable mSum, and mSum = mSum + 1 is not an atomic operation. And it should be obvious that printing to console, incrementing a variable and then printing to console again is not atomic all the more :) There are many possible ways the threads may interlace. For example:

1) mSum = 0 [Thread1 is working]

2) mSum = 1 [Thread1 is working]

3) mSum = 2 [Thread2 is working]

4) ...

5) mSum = 59 [Thread2 is working] and it gets pre-empted after "Hello..."

6) mSum = 60 [Thread1 is working]

7) mSum = 61 [Thread1 is working]

8) ...

9) mSum = 64 [Thread2 is working] awaken just before incrementation line Thread2 continues, and calculates 65

In 5) Thread2 could've been pre-empted even after reading mSum from memory in mSum = mSum + 1 but before calculating mSum + 1.

like image 152
BartoszKP Avatar answered Oct 18 '22 10:10

BartoszKP


Because you are using multiple threads, the value may well have changed between the first and second Console.WriteLine call.

If you want to make sure you are reporting the correct value that the addition will use, you'll have to use a lock.

like image 22
Sean Airey Avatar answered Oct 18 '22 10:10

Sean Airey