Orion Edwards
Orion Edwards

Reputation: 123612

C# Multithreading and reordering - what's going on here?

I have the following code:

    public static System.Int32 i = 0;

    static void Main(string[] args)
    {
        new Thread(Worker1) { IsBackground = true }.Start();
        new Thread(Worker2) { IsBackground = true }.Start();

        Console.WriteLine("Running... Press enter to quit");
        Console.ReadLine();
    }

    static void Worker1(object _)
    {
        while (true)
        {
            var oldValue = i;
            i++;
            var newValue = i;

            if (newValue < oldValue)
                Console.WriteLine("{2}, i++ went backwards! oldValue={0}, newValue={1}!", oldValue, newValue, DateTime.Now.ToString("HH:MM:ss.fff"));
        }
    }

    static void Worker2(object _)
    {
        while (true)
        {
            i++;
        }
    }

When run, this produces output that looks like this:

17:08:17.020, i++ went backwards! oldValue=6124653, newValue=6113984!
17:08:17.057, i++ went backwards! oldValue=18764535, newValue=18752368!
17:08:17.086, i++ went backwards! oldValue=27236177, newValue=27236176!
17:08:17.087, i++ went backwards! oldValue=27550457, newValue=27535008!
17:08:17.130, i++ went backwards! oldValue=40251349, newValue=40235492!
17:08:17.137, i++ went backwards! oldValue=42339974, newValue=42323786!
17:08:17.142, i++ went backwards! oldValue=43828602, newValue=43828436!
17:08:17.149, i++ went backwards! oldValue=45969702, newValue=45959111!
17:08:17.158, i++ went backwards! oldValue=48705847, newValue=48705549!
17:08:17.230, i++ went backwards! oldValue=71199684, newValue=71199674!

Note: this is being run on a quad-core i7 with hyperthreading on windows 7

As far as I can tell, either:

Can anyone shed some light on it?


To clarify the question: I am deliberately looking for code which reproduces memory reordering bugs as part of an educational excercise. There are many ways to fix this, I was mainly interested in analyzing what was happening.

@Tymek has shown the (blindingly obvious now that it's pointed out) answer below.

Upvotes: 2

Views: 226

Answers (3)

tmaj
tmaj

Reputation: 34947

You surely know that i++ is not atomic and looks really like something like this:

  • read i from memory to a register,
  • increment the register,
  • write register to memory where i is,
  • return i;

This sequence can be interrupted at any stage.

Having this in mind, one case which generates your message of doom is a follows:

  • Let's i be 100;
  • Thread 2 reads 100 and is interrupted;
  • Thread 1 executes for a while and increments i to 120 (few full loops), then reads i as 120, reads i and increments it to 121 and is interrupted before var newValue = i;;
  • Thread 2 increments i to 101 and is interrupted;
  • Thread one runs and executes var newValue = i <- this will read in 101;

Now oldValue is 120 and newValue is 101.

Upvotes: 4

user854301
user854301

Reputation: 5483

To avoid this issue you could use Interlocked.Increment it will garantee that operation will be atomic and thread safe. When you use i++ it is not atomary and value of it could be changed during incrementation (from another thread e.g.).

Upvotes: 1

Tomtom
Tomtom

Reputation: 9384

If u want that the assignment of the old-value, the incrementation of i and the assignment of the new-value happens "at the same time" u should use a lock. Just introduce a private variable and then use the lock on this. This looks something like:

lock(lockObj)
{
  var oldValue = i;             
  i++;             
  var newValue = i;
}

Upvotes: 1

Related Questions