Some multi-threading gotchas

Most programmers know that they must make their code thread-safe when accessing shared data from multiple threads at the same time.
However, there are some cases which are not so obvious and which might cause very subtle bugs that appear only in some particular circumstances.

Below is a list of such cases that I started to ‘collect’. Probably for some programmers the next examples are nothing new, but at least for me they were ‘unexpected’, at least initially when I read about them:

Example 1:

    class Foo
    {
        int _answer;
        bool _complete;

        public void A()
        {
            _answer = 123;
            _complete = true;
        }

        public void B()
        {
            if (_complete) Console.WriteLine(_answer);
        }
    }

Seems harmless and thread-safe, right? Well, not allways 🙂 – if A() and B() are executed in parallel from multiple threads, in some particular cases Console.WriteLine will display… 0 (zero).
Source and explanation: http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility – in short, the two assignments could be reordered on some processors.

Example 2:

    class Program
    {
        static void Main()
        {
            bool complete = false;
            var t = new Thread(() =>
                {
                    bool toggle = false;
                    while (!complete) // waiting for the flag to be set...
                    {
                        toggle = !toggle; // do something 🙂
                    }
                });
            t.Start();

            Thread.Sleep(1000); // wait a bit to let the thread to run..
            complete = true; // 'signal' the thread

            t.Join();        // wait the thread to complete - blocks indefinitely (!)
        }
    }

The thread in the above code will never complete (if the code is compiled in release mode with optimizations on).
Details and a solution again at: http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility

Example 3:

    class Test
    {
        private bool _flag = true;
        public void Run()
        {
            // Set _flag to false on another thread
            new Thread(() => { _flag = false; }).Start();
            // Poll the _flag field until it is set to false
            while (_flag) ;
            // The loop might never terminate!
        }
    }

Notice the ‘might’ – sometimes it will terminate, sometimes the .NET JIT compiler will decide to ‘cache’ the _flag like so:
if (_flag) { while (true); }
I managed to reproduce the ‘never-ending loop’ by compiling on release mode, optimizations enabled, on 1 out of 3 tries.
Source: http://msdn.microsoft.com/en-us/magazine/jj883956.aspx

Example 4:

    class Program
    {
        class Test
        {
            private decimal _x;

            public void Write(decimal newValue)
            {
                _x = newValue; // just a 'simple' write
            }

            public void Read()
            {
                decimal v = _x; // read the written value
                Console.WriteLine(v);
            }

        }

        static void Main()
        {
            Test f = new Test();

            const int numThreads = 100;
            Thread[] threads = new Thread[numThreads];
            for (int i = 0; i < numThreads; i++)
            {
                if (i % 2 == 0)
                {
                    int k = i;
                    threads[i] = new Thread(() => f.Write(8999999999999999999999999999m));
                }
                else
                {
                    threads[i] = new Thread(f.Read);
                }
            }
            // some threads write while other read from the same memory location
            for (int i = 0; i < numThreads; i++)
            {
                threads[i].Start();
            }

            // wait for all threads to complete
            for (int i = 0; i < numThreads; i++)
            {
                threads[i].Join();
            }
            Console.ReadLine();
        }
    }

Is it possible to read a value that was never written? Yes 🙂
Source and explanation: http://www.bluebytesoftware.com/blog/2006/02/08/ThreadsafetyTornReadsAndTheLike.aspx
Why? decimal _x = newValue;
is not an atomic operation, even on 64-bit processors (decimal is stored on 128 bits).
Even a long x = 1231243124214423; might be non-atomic on 32-bits processors.
An example that better reproduces the issue described above can be found at: http://stackoverflow.com/questions/11360645/should-i-always-synchronize-access-to-all-double-field-property-variables-that

The conclusion? Try to avoid sharing state and instances between multiple threads, but if you can’t, make sure you read twice the excellent article on this topic by Joseph Albahari: http://www.albahari.com/threading/
Do not assume the code will always run on x86-x64 architecture where you can make some some assumptions – even if .NET 4.5 does not support Itanium processors anymore, recently .NET code started to execute on ARM processors, that have their own different instruction set.

Advertisements
This entry was posted in .NET, C# and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s