用于C#的快速线程安全随机数发生器

我需要在多个正在运行的线程中快速生成随机浮点数。 我尝试过使用System.Random ,但它对我的需求来说太慢了,它在多个线程中返回相同的数字。 (当我在一个线程中运行我的应用程序时,它工作正常。)另外,我需要确保生成的数字在0到100之间。

这就是我现在正在尝试的事情:

 number = random.NextDouble() * 100; 

我该怎么办呢?

这是我对它的看法(需要.net 4.0):

 public static class RandomGenerator { private static object locker = new object(); private static Random seedGenerator = new Random(Environment.TickCount); public static double GetRandomNumber() { int seed; lock (locker) { seed = seedGenerator.Next(int.MinValue, int.MaxValue); } var random = new Random(seed); return random.NextDouble(); } } 

并且测试检查1000次迭代每个值是唯一的:

 [TestFixture] public class RandomGeneratorTests { [Test] public void GetRandomNumber() { var collection = new BlockingCollection(); Parallel.ForEach(Enumerable.Range(0, 1000), i => { var random = RandomGenerator.GetRandomNumber(); collection.Add(random); }); CollectionAssert.AllItemsAreUnique(collection); } } 

我不保证它永远不会返回重复值,但我已经运行了10000次迭代的测试并且它通过了测试。

我使用windows cryptoAPI获得良好的随机数。 为了提高性能,我只需要调用一个8KB的随机数据块并从中分配数字,而不是为每个数字调用cryptoAPI。 与普通随机相比,最终不确定性能是什么。 但随机化要好得多(有关Windows CryptoAPI的详细信息,请访问互联网)

这是代码;

 // UNIT RandomNumberGeneratorBase using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace FastLibrary { public abstract class RandomNumberGeneratorBase { private int _byteBufSize; private byte[] _buf; private int _idx; private int _lastsize; public RandomNumberGeneratorBase(int bufSize = 8092) { _byteBufSize = bufSize; _buf = new byte[_byteBufSize]; _idx = _byteBufSize; } protected abstract void GetNewBuf(byte[] buf); private void CheckBuf(int bytesFreeNeeded = 1) { _idx += _lastsize; _lastsize = bytesFreeNeeded; if (_idx + bytesFreeNeeded < _byteBufSize) { return; } GetNewBuf(_buf); _idx = 0; _lastsize = 0; } public byte GetRandomByteStartAtZero(int belowValue) { return (byte)(Math.Round(((double)GetRandomByte() * (belowValue - 1)) / 255)); } public int GetRandomIntStartAtZero(int belowValue) { return (int)(Math.Round(((double)GetRandomUInt32() * (double)(belowValue - 1)) / (double)uint.MaxValue)); } public byte GetRandomByte() { CheckBuf(); return _buf[_idx]; } public bool GetRandomBool() { CheckBuf(); return _buf[_idx] > 127; } public ulong GetRandomULong() { CheckBuf(sizeof(ulong)); return BitConverter.ToUInt64(_buf, _idx); } public int GetRandomInt() { CheckBuf(sizeof(int)); return BitConverter.ToInt32(_buf, _idx); } ///  /// Double from 0 to 1 (might be zero, will never be 1) ///  public double GetRandomDouble() { return GetRandomUInt32() / (1d + UInt32.MaxValue); } ///  /// Float from 0 to 1 (might be zero, will never be 1) ///  public float GetRandomFloat() { return GetRandomUInt32() / (1f + UInt32.MaxValue); } public uint GetRandomUInt32() { CheckBuf(sizeof(UInt32)); return BitConverter.ToUInt32(_buf, _idx); } } } // UNIT StrongRandomNumberGenerator using System; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; namespace FastLibrary { public sealed class StrongRandomNumberGenerator : RandomNumberGeneratorBase { private RNGCryptoServiceProvider _rnd; public StrongRandomNumberGenerator() { _rnd = new RNGCryptoServiceProvider(); } protected override void GetNewBuf(byte[] buf) { _rnd.GetBytes(buf); } } } 

如果Random给你相同的数字,你可能错误地使用它,或者通过紧密连续创建许多实例(意味着它们都使用相同的种子,因此生成相同的序列),或者使用单个实例跨越多个线程(从而“破坏”该实例,因为它对于multithreading使用是不安全的)。

如果Random在单个线程中运行时的速度和随机性足够好,那么您可以尝试将其包装在ThreadLocal以确保multithreading场景中每个线程的单独实例:

 var number = _rng.Value.NextDouble() * 100; // ... private static int _staticSeed = Environment.TickCount; private static readonly ThreadLocal _rng = new ThreadLocal(() => { int seed = Interlocked.Increment(ref _staticSeed) & 0x7FFFFFFF; return new Random(seed); });