如何制作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语句。这个答案呢?它链接到锁定是最后一个需要使用的工具,尤其是在这种情况下