.net core 多線程下使用 Random 會出現bug

.net core 多線程下使用 Random 會出現的bug

先看原文:
Working with System.Random and threads safely in .NET Core and .NET Framework
https://andrewlock.net/building-a-thread-safe-random-implementation-for-dotnet-framework/

我的結論:
.net 6 之前,如果習慣構建了一個靜態的 Random 對象,然後業務代碼裏直接使用 random.Next() 方法,多線程競爭下,會有一定概率返回0。
.net 6 時,修正了這個默認創建時的多線程存在的bug。但又沒完全解決。如果這個公共的 Random 方法初始化的適合使用了自定義隨機種子,則還是會存在多線程競爭下出現返回0情況。
原文提供了在 .net 6 之前版本的一些解決辦法,我的建議是使用 random.Next(1, 10000) 這樣的方法生成隨機數。

原文測試用代碼片段[改]:


using System;
using System.Linq;
using System.Threading.Tasks;

public class Program {

    public static void Main() {
        // ⚠ This isn't safe, don't do it ⚠
        Random rng = new Random(); // create a shared Random instance
        Parallel.For(0, 10, x =>  // run in parallel
        {
            var numbers = new int[10_000];
            for (int i = 0; i < numbers.Length; ++i)
            {
                numbers[i] = rng.Next(); // Fetch 10,000 random numbers, to trigger the thread-safety issues
            }

            var numZeros = numbers.Count(x => x == 0); // how many issues were there?
            var avg = numbers.Average();
            Console.WriteLine($"Received {numZeros} zeroes  avg:{avg} {numbers[0]}");
        });
    }
}

當然好奇的腳步不止於此,爲什麼文章說的,大部分人習慣直接調用 .Next() 方法,而不是 .Next(min,max) 這樣的方法呢?
我姑且認爲是性能問題,於是增加了一個性能測試代碼片段,用於生成 (1,100)區間內的隨機數:

    public class RandomT
    {
        System.Random random1 = new System.Random(1);
        System.Random random2 = new System.Random(1);

        System.Random random11 = new System.Random();
        System.Random random22 = new System.Random();

        [Benchmark]
        public int RandomSeedByRange() => random1.Next(1, 100);

        [Benchmark]
        public int RandomSeedByMod() => random2.Next() % 100 + 1;

        [Benchmark]
        public int RandomDefaultByRange() => random11.Next(1, 100);

        [Benchmark]
        public int RandomDefaultByMod() => random22.Next() % 100 + 1;
    }

BenchmarkDotNet=v0.13.2, OS=ubuntu 20.04
Intel Xeon Gold 6133 CPU 2.50GHz, 1 CPU, 2 logical and 2 physical cores
.NET SDK=6.0.200
  [Host]     : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT AVX2
  DefaultJob : .NET 6.0.2 (6.0.222.6406), X64 RyuJIT AVX2

Method Mean Error StdDev
RandomSeedByRange 15.096 ns 0.1449 ns 0.1210 ns
RandomSeedByMod 14.759 ns 0.1298 ns 0.1014 ns
RandomDefaultByRange 9.310 ns 0.0702 ns 0.0548 ns
RandomDefaultByMod 4.925 ns 0.1387 ns 0.2393 ns

性能果然是直接使用 .Next() 最優。兩者差距有一倍。
但是,指定隨機種子後,兩者就沒有差距了,這就有點讓人想不通了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章