如何制作c#线程安全的随机数生成器

如何制作c#线程安全的随机数生成器,c#,multithreading,.net-core,C#,Multithreading,.net Core,我的代码中有一个循环 Parallel.For(0, Cnts.MosqPopulation, i => { DoWork() }); 但是,在DoWork()函数中,有多个对随机数生成器的调用,其定义如下: public static class Utils { public static readonly Random random = new Random(); } 它是静态实例,所以它只被播种一次。我可以在整个代码中使用它 根据MSDN和其他stackoverflow

我的代码中有一个循环

Parallel.For(0, Cnts.MosqPopulation, i => { DoWork() });
但是,在
DoWork()
函数中,有多个对随机数生成器的调用,其定义如下:

public static class Utils
{
    public static readonly Random random = new Random();

}
它是静态实例,所以它只被播种一次。我可以在整个代码中使用它

根据MSDN和其他stackoverflow线程,这不是线程安全的。事实上,我注意到有时我的代码中断,随机数生成器开始生成全零(根据MSDN文档)

还有其他stackoverflow线程,但它们都很旧,而且实现速度很慢。我不能在生成数字上浪费时间,因为这个程序是一个科学计算,正在运行数百个模拟

自从2.0时代以来,我就没有使用过.net,我不知道该语言是如何演变成能够快速、高效、线程安全的RNG的

以下是前面的线程:

注意:因为我需要一个快速的实现,所以我不能使用相当慢的
RNGCryptoServiceProvider


注2:我没有最低限度的工作代码。我甚至不知道从哪里开始,因为我不知道线程安全是如何工作的,也不知道c#的高级知识。所以,看起来我确实在要求一个完整的解决方案

使用
ThreadStatic
属性和自定义getter,每个线程将获得一个
Random
实例。如果这是不可接受的,请使用锁

public static class Utils
{
    [ThreadStatic]
    private static Random __random;

    public static Random Random => __random??(__random=new Random());
}
ThreadStatic
属性不会在每个线程上运行初始值设定项,因此您有责任在访问器中执行此操作。还可以考虑种子初始值设定项,您可以使用

new Random((int) ((1+Thread.CurrentThread.ManagedThreadId) * DateTime.UtcNow.Ticks) )

如果您知道并行运行的线程数,则可以:

Random rand = new Random();
var randomNums = Enumerable.Range(0, Cnts.MosqPopulation)
                           .Select(_ => rand.Next()).ToList();
Parallel.For(0, Cnts.MosqPopulation, i => 
{
    Random localRand = new Random(randomNums[i]);
    DoWork();
});

不确定所得到的分布与均匀分布的区别是什么。

< P>我会考虑这样的事情:

private static int _tracker = 0;

private static ThreadLocal<Random> _random = new ThreadLocal<Random>(() => {
    var seed = (int)(Environment.TickCount & 0xFFFFFF00 | (byte)(Interlocked.Increment(ref _tracker) % 255));
    var random = new Random(seed);
    return random;
});
private static int\u tracker=0;
private static ThreadLocal\u random=new ThreadLocal(()=>{
var seed=(int)(Environment.TickCount&0xFFFFFF00 |(byte)(Interlocked.Increment(ref _tracker)%255));
var random=新随机(种子);
返回随机;
});
现在我对
ThreadStatic
不太感兴趣。我们有比使用
ThreadLocal
更好的工具。只需在并行循环中使用
\u random.Value
,它将为每个线程提供一个新的
random

它结合了一个原子递增的值以及使用
Environment.TickCount
的默认行为。递增的值用于解决两个随机数获得相同种子的问题。注意,这种方法只允许创建255个随机数。如果需要更多,请更改遮罩的大小


正如您已经指出的,这对于安全目的是不可用的。

您可以从
Random
继承来构建线程安全的随机类

public class ThreadsafeRandom : Random
{
    private readonly object _lock = new object();

    public ThreadsafeRandom() : base() { }
    public ThreadsafeRandom( int Seed ) : base( Seed ) { }

    public override int Next()
    {
        lock ( _lock )
        {
            return base.Next();
        }
    }

    public override int Next( int maxValue )
    {
        lock ( _lock )
        {
            return base.Next( maxValue );
        }
    }

    public override int Next( int minValue, int maxValue )
    {
        lock ( _lock )
        {
            return base.Next( minValue, maxValue );
        }
    }

    public override void NextBytes( byte[ ] buffer )
    {
        lock ( _lock )
        {
            base.NextBytes( buffer );
        }
    }

    public override double NextDouble()
    {
        lock ( _lock )
        {
            return base.NextDouble();
        }
    }
}
并使用该类的实例

public static class Utils
{
    public static readonly Random random = new ThreadsafeRandom();

}

为什么你链接的帖子中的解决方案不适合你?有什么特别的原因吗?根据他们的评论,其中一些确实不是线程安全的。其他的太慢了。我希望前面的答案有一个更现代的版本。那么这一个怎么样:@DirkVollmar评论提到如果线程创建太快,可能会生成相同的随机数。这在我的代码中是很可能的,因为
DoWork()
非常简单,只有几个if语句。这个答案呢?它链接到锁定是最后一个需要使用的工具,尤其是在这种情况下