1

I was trying to investigate a simple piece of code with two threads accessing a shared integer variable, one incrementing and the other decrementing it:

static int n = 0;
static void Main()
{
    var up = new Task(() =>
    {
        for (int j = 0; j < 400000; j++)
           ++n;
    });

    up.Start();
    for (int i = 0; i < 400000; i++)
        --n;
    up.Wait();

    Console.WriteLine(n);
    Console.ReadKey();
}

I've read a number of times that C# increment/decrement operations are not thread-safe and should be replaced with Interlocked.Increment and Decrement methods. That indeed works however I when I try to set a breakpoint and check the disassembly window, it looks as if the ++ and -- operators are atomic, their assembly equivalents are

inc dword ptr ds:[29A0C9Ch]

and

dec dword ptr ds:[29A0C9Ch]

respectively. So why is it then that the code above sometimes outputs a non-zero value?

sergefr
  • 21
  • 1

2 Answers2

2

Because they are not atomic to start with: How come INC instruction of x86 is not atomic?

Interlocked synchronizes the caches on top and makes sure no interruption happens.

why is i++ not thread safe on a single core machine?

has some more details.

Community
  • 1
  • 1
TomTom
  • 61,059
  • 10
  • 88
  • 148
  • Thanks, that certainly explains the issue. Didn't think about atomicity of assembly instructions. – sergefr Aug 11 '14 at 07:55
1

So why is it then that the code above sometimes outputs a non-zero value?

Because inc and dec are not atomic. The processor will translate these into a lower-level instructions (e.g., inc => read + add + store)

Community
  • 1
  • 1
dcastro
  • 66,540
  • 21
  • 145
  • 155