我的代码中有一个循环
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。
以下是以前的主题:
Is C# Random Number Generator thread safe?
Correct way to use Random in multithread application
Fast thread-safe random number generator for C#
注意:因为我需要快速实现,所以我不能使用速度相当慢的RNGCryptoServiceProvider
。
注2:我没有最小的工作代码。我甚至不知道从哪里开始,因为我不知道线程安全如何工作或具有高级的c#知识。所以看起来我似乎要求一个完整的解决方案。
答案 0 :(得分:2)
使用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) )
答案 1 :(得分:1)
我的实现结合了其他答案中的最佳方法(请参阅课堂文档中的设计说明)。
/// <summary>
/// DotNet Random is not ThreadSafe so we need ThreadSafeRandom.
/// See also: https://stackoverflow.com/questions/3049467/is-c-sharp-random-number-generator-thread-safe.
/// Design notes:
/// 1. Uses own Random for each thread (thread local).
/// 2. Seed can be set in ThreadSafeRandom ctor. Note: Be careful - one seed for all threads can lead same values for several threads.
/// 3. ThreadSafeRandom implements Random class for simple usage instead ordinary Random.
/// 4. ThreadSafeRandom can be used by global static instance. Example: `int randomInt = ThreadSafeRandom.Global.Next()`.
/// </summary>
public class ThreadSafeRandom : Random
{
/// <summary>
/// Gets global static instance.
/// </summary>
public static ThreadSafeRandom Global { get; } = new ThreadSafeRandom();
// Thread local Random is safe to use on that thread.
private readonly ThreadLocal<Random> _threadLocalRandom;
/// <summary>
/// Initializes a new instance of the <see cref="ThreadSafeRandom"/> class.
/// </summary>
/// <param name="seed">Optional seed for <see cref="Random"/>. If not provided then random seed will be used.</param>
public ThreadSafeRandom(int? seed = null)
{
_threadLocalRandom = new ThreadLocal<Random>(() => seed != null ? new Random(seed.Value) : new Random());
}
/// <inheritdoc />
public override int Next() => _threadLocalRandom.Value.Next();
/// <inheritdoc />
public override int Next(int maxValue) => _threadLocalRandom.Value.Next(maxValue);
/// <inheritdoc />
public override int Next(int minValue, int maxValue) => _threadLocalRandom.Value.Next(minValue, maxValue);
/// <inheritdoc />
public override void NextBytes(byte[] buffer) => _threadLocalRandom.Value.NextBytes(buffer);
/// <inheritdoc />
public override void NextBytes(Span<byte> buffer) => _threadLocalRandom.Value.NextBytes(buffer);
/// <inheritdoc />
public override double NextDouble() => _threadLocalRandom.Value.NextDouble();
}
答案 2 :(得分:0)
如果您了解并行运行的线程数,则可能有效:
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();
});
不确定所得分布与统一分布之间的区别是多么难以区分。
答案 3 :(得分:0)
我会考虑这样的事情:
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;
});
这些天我不是ThreadStatic
的忠实粉丝。我们有比使用ThreadLocal
更好的工具。只需在并行循环中使用_random.Value
,它就会为每个线程提供一个新的Random
。
它结合了原子递增值以及使用Environemnt.TickCount
的默认行为。递增值用于解决两个Random获得相同种子的问题。请注意,此方法仅允许创建255个randoms。如果您需要更多,请更改面具的大小。
正如您已经指出的那样,这不能用于安全目的。
答案 4 :(得分:-1)
您可以从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();
}