如何在Nullable<long>上造成撕裂?

问题描述 投票:0回答:1

如果我在 x86(32 位)模式(最近的 Windows 11 计算机上的 .NET Framework 4.7.2)下运行以下代码,那么,正如预期的那样,它会写入控制台(证明读出的数据不是其中之一)由于撕裂而写入两个值:

这是预料之中的。根据这些参考资料,以及 Lippert 在 https://ericlippert.com/2012/12/20/nullable-micro-optimizations-part-one/ 的评论,我预计

Nullable<long>
also 表现出撕裂.

但是,它没有(取消注释下面的代码以进行重现)。现在,当然,我确信这是合法的。事实上,他们告诉你某件事不安全,并不意味着它肯定不起作用——它起作用是完全有效的。但我很好奇为什么没有发生撕裂......

有没有办法导致撕裂

Nullable<long>

using System;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    public class Program
    {
        public static void Main(string[] args)
        {
            long bob = 0;
            //long? bob = 0;

            long small = 0;
            long large = long.MaxValue;


            Task.Run(() =>
            {
                do
                {
                    bob = small;
                    bob = large;
                } while (true);
            });

            for (long i = 0; i < long.MaxValue - 2; i++)
            {
                var value = bob;

                if (value != small && value != large)
                {
                    Console.WriteLine("very bad");
                }
            }

            Console.WriteLine("finished");
            Console.ReadLine();
        }
    }
}
c# nullable tearing
1个回答
0
投票

有没有办法导致 Nullable 撕裂?

是的。

通过这段代码,我们在两个线程之间引入了重复的竞争。在一个线程中,我们将值设置为

null
,在另一个线程中将值设置为
2

public struct MyNullable<T> where T : struct {
    public bool hasValue;
    public T value;

    public override string ToString() {
        return hasValue + " " + value.ToString();
    }
}

static void Main() {
    int tries = 10_000_000;

    long? l = 0;
    var listTears = new List<long?>();

    Action<Barrier> postPhase = _ => {
        var cast = Unsafe.As<long?, MyNullable<long>>(ref l);
        if (cast.hasValue == false && cast.value == 2) {
            var capture = l;
            listTears.Add(capture);
            l = 0; // reset for next race
        }
    };

    var barrier = new Barrier(2, postPhase);
    var listOfThreads = new List<Thread>();
    var t1 = new Thread(() => {
        for (int j = 0; j < tries; j++) {
            l = null;
            barrier.SignalAndWait();
        }

    });

    var t2 = new Thread(() => {
        for (int j = 0; j < tries; j++) {
            l = 2;
            barrier.SignalAndWait();
        }
    });

    t1.Start();
    t2.Start();

    t1.Join();
    t2.Join();

    var countTears = listTears.Count;
    Console.WriteLine($"Struct tears: {countTears}");


    var takeTenTears = listTears
        .Take(10)
        .Select(x => Unsafe.As<long?, MyNullable<long>>(ref x));

    foreach (var element in takeTenTears) {
        Console.WriteLine(element.ToString());
    }

    // False 2
    //False 2
    //False 2
    //False 2
    //False 2
    //False 2
    //False 2
    //False 2
    //False 2
    //False 2
}
© www.soinside.com 2019 - 2024. All rights reserved.