.NET 6 新特性 —— Random.Shared

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.NET 6 新特性 —— Random.Shared相关的知识,希望对你有一定的参考价值。

.NET 6 新特性 —— Random.Shared

Intro

最近微软发了一篇 .NET 6 性能提升的博客文章,里面提到了很多有趣的东西,其中有一个是 Random.Shared

这是一个只读的静态属性,并且是一个线程安全的对象,这个东西可以帮助我们简化 Random 对象的使用

Before

首先我们需要知道 Random 不是线程安全的,所以我们如果要在多线程下用 Random 的话,通常需要考虑线程安全问题

既然不是线程安全的,那我们用的时候创建一个就好了,每次都 new 一个,但是这样的话一来代码不够简洁,二来可能会创建比较多的 Random 对象,那么不使用的时候每次 new 要怎么做呢?

比较简单的做法就是加一个锁,如下所示:

public static class RandomGen1
{
    private static Random _inst = new Random();

    public static int Next()
    {
        lock (_inst) return _inst.Next();
    }
}

但是这样的话会导致获取锁的效率相对来说会比较低,每次还需要去先拿到锁,所以也有别的解法:

public static class RandomGen2
{
    private static Random _global = new Random();
    [ThreadStatic]
    private static Random _local;

    public static int Next()
    {
        Random inst = _local;
        if (inst == null)
        {
            int seed;
            lock (_global) seed = _global.Next();
            _local = inst = new Random(seed);
        }
        return inst.Next();
    }
}

上面是一个微软推荐的实现,那么为什么要两个 Random 对象呢,Random 对象产生随机数依赖于一个 seed,默认是使用当前时间,如果时间特别接近的话,会发现即使是不同的 Random 对象产生的随机数有可能是一样的

上面的 _global 对象是用于产生随机的 seed,避免使用默认的 seed,_local 是一个被标记为 ThreadStatic 的对象,每一个线程会拥有一个单独的 Random 对象从而避免线程安全问题。

Sample

有了 Random.Shared 之后,我们就不需要上面的封装了

Console.WriteLine(Random.Shared.Next(1, 100));

Parallel.For(0, Environment.ProcessorCount, _ =>
{
    var thread = new Thread(() => 
    {
        var arr = new int[10];
        for (var i = 0; i < arr.Length; i++)
        {
            arr[i] = Random.Shared.Next(1, 100);
        }
        Console.WriteLine(arr.Average());
    });
    thread.Start();
});

Random.Shared 的实现也是类似于上面的 ThreadStatic 的解法,但是是从 CLR 的底层去实现的,会比上面的实现方式更为高效,实现代码可以参考 Github:https://github.com/dotnet/runtime/blob/v6.0.0-preview.7.21377.19/src/libraries/System.Private.CoreLib/src/System/Random.cs

More

使用新的 Random.Shared 对象之后就移除掉代码中大量的 new Random 或者自己封装的 Random 了,代码更加简洁了~~

References

  • https://github.com/dotnet/runtime/issues/43887

  • https://github.com/dotnet/runtime/pull/50297

  • https://github.com/dotnet/runtime/pull/50297/files#diff-6fa7e54f57878bb019a11332aeeb42c75430a0ac87c78cdfa9ce382137b3d851R51

  • https://github.com/dotnet/runtime/blob/v6.0.0-preview.7.21377.19/src/libraries/System.Private.CoreLib/src/System/Random.cs

  • https://devblogs.microsoft.com/pfxteam/getting-random-numbers-in-a-thread-safe-way/

  • https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-6/

以上是关于.NET 6 新特性 —— Random.Shared的主要内容,如果未能解决你的问题,请参考以下文章

.NET 6新特性试用 | 总结:我最喜欢的5个特性

.NET 6 新特性 WaitAsync

.NET 6新特性试用 | PeriodicTimer

.NET 6 新特性 Parallel ForEachAsync

.NET 6新特性试用 | 可空引用类型

.NET 6 新特性 —— Random.Shared