在 C# 中重现十进制的撕裂读取

Sti*_*tig 4 c# atomic thread-safety lock-free

眼见为实。任何人都可以重现读取撕裂的小数的程序吗?我尝试旋转多个线程,在 1 和 2 之间更改相同的小数。我没有捕获任何与 1 或 2 不同的读取。

我希望看到读取器线程看不到写入器线程的原子更改,因此该值应该与 1 或 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());
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*son 6

此代码将演示 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

它在发布版本中比调试版本中发生得更快。

我认为您在测试代码中没有看到撕裂读取的原因是因为您只更改了 0 和 1 之间的值。测试期间更改的位很可能都在用于存储的同一个字中内部的值,并且对单词的访问是原子的。

通过更改 1 到 10000000000 之间的值,我们强制在两个不同的字中更改位,从而可以观察到撕裂的读取。