Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reproduce torn reads of decimal in C#

Seeing is believing. Can anyone reproduce a program that reads a torn decimal? I tried spinning up multiple threads changing the same decimal between 1 and 2. I did not catch any reads different from 1 or 2.

I like to see that a reader thread does not see a atomic change from a writer thread, so the value should be something different from 1 or 2.

void TornDecimalReadTest()
{
    decimal sharedDecimal = 1;
    int threadCount = 100;
    var threads = new List<Thread>();

    for (int i = 0; i < threadCount; i++)
    {
        int threadId = i;
        var thread = new Thread(() =>
        {
            Thread.Sleep(5000);

            decimal newValue = threadId % 2 == 0 ? 1 : 2;
            bool isWriterThread = threadId % 2 == 0;

            Console.WriteLine("Writer : " + isWriterThread +
                " - will set value " + newValue);

            for (int j = 0; j < 1000000; j++)
            {
                if (isWriterThread)
                    sharedDecimal = newValue;

                decimal decimalRead = sharedDecimal;

                if (decimalRead != 1 && decimalRead != 2)
                    Console.WriteLine(decimalRead);
            }
        });

        threads.Add(thread);
    }

    threads.ForEach(x => x.Start());
    threads.ForEach(x => x.Join());
}
like image 456
Stig Avatar asked Oct 21 '25 20:10

Stig


1 Answers

This code will demonstrate a torn read of a Decimal:

using System;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        void run()
        {
            Task.Run((Action) setter);
            Task.Run((Action) checker);

            Console.WriteLine("Press <ENTER> to stop");
            Console.ReadLine();
        }

        void setter()
        {
            while (true)
            {
                d = VALUE1;
                d = VALUE2;
            }
        }

        void checker()
        {
            for (int count = 0;; ++count)
            {
                var t = d;

                if (t != VALUE1 && t != VALUE2)
                    Console.WriteLine("Value is torn after {0} iterations: {1}", count, t);
            }
        }

        static void Main()
        {
            new Program().run();
        }

        Decimal d;

        const Decimal VALUE1 = 1m;
        const Decimal VALUE2 = 10000000000m;
    }
}

It happens faster in a release build than a debug build.

I think the reason that you weren't seeing a torn read in your test code is because you were only changing the value between 0 and 1. It's likely that the bits being changed during your test are all in the same word being used to store the value internally, and accesses to words are atomic.

By changing the value between 1 and 10000000000, we force bits to change in two different words, allowing a torn read to be observed.

like image 184
Matthew Watson Avatar answered Oct 24 '25 12:10

Matthew Watson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!