Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the following C# multi-threaded code does not output zero though it does in debugger?

class Program
{
    private static volatile int value;

    public static void Increment()
    {
        for (int i =0; i <100000; i++)
        {
            value++;
        }
    }

    public static void Decrement()
    {
        for (int j =0 ; j < 100000; j++)
        {
            value--;
        }
    }

    public static void ThreadTest()
    {
        value = 0;

        var incrementThread = new Thread(Increment);

        var decrementThread = new Thread(Decrement);

        incrementThread.Start();

        decrementThread.Start();

        incrementThread.Join();

        decrementThread.Join();

        Console.WriteLine("Value of value {0}", value);
    }

    static void Main(string[] args)
    {
        ThreadTest();
    }
}
like image 767
user673979 Avatar asked Mar 23 '11 22:03

user673979


People also ask

Why do we write in C?

C programming language uses blocks to separate pieces of code performing different tasks. This helps make programming easier and keeps the code clean. Thus, the code is easy to understand even for those who are starting out. C is used in embedded programming, which is used to control micro-controllers.

What is C and why we use C?

C is a general-purpose programming language and can efficiently work on enterprise applications, games, graphics, and applications requiring calculations, etc. C language has a rich library which provides a number of built-in functions. It also offers dynamic memory allocation.

Why C is named so?

Because a and b and c , so it's name is C. C came out of Ken Thompson's Unix project at AT&T. He originally wrote Unix in assembly language. He wrote a language in assembly called B that ran on Unix, and was a subset of an existing language called BCPL.

Why C function is used?

A function is a block of code which only runs when it is called. You can pass data, known as parameters, into a function. Functions are used to perform certain actions, and they are important for reusing code: Define the code once, and use it many times.


2 Answers

Because it is not supposed to... ++ and -- are not atomic operations (unlike Interlocked.XXXX opreations - Interlocked.Increment).

If you write down each step of ++ and -- and see how both can be intermixed by different threads you'll see why:

increment

1: load value to temp
2: add temp, 1
3: store temp to value

decrement

4: load value to temp2
5: substruct temp2, 1
6: store temp2 to value

So if order is 1,2,3,4,5,6 you get value = 0; but if order is 1,2,4,5,6,3 you get value = 1.

like image 73
Alexei Levenkov Avatar answered Sep 23 '22 13:09

Alexei Levenkov


Only trying to make the things simpler... I fought this issue back in the day as well :D

Volatile ensure you read the latest value, and when you write all threads see that new value (and that is what volatile operations are for), but it doesn't ensure that between the read and the write, other thread is not going to modify the value. In the other hand, Interlocked (that provides atomic operations) does ensure it.

Volatile operations are good, when for example a thread or threads read and other modify. For example if you have a volatile Boolean _disposed flag in your class, so if one thread dispose it, it's marked as disposed for all threads straight away.

like image 33
vtortola Avatar answered Sep 23 '22 13:09

vtortola