2012-02-16 89 views
-1

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

这里是我现在想:

number = random.NextDouble() * 100; 

我应该试试什么?

+4

如果随机给出的数字始终相同,那么您很可能没有正确使用它。还要注意'Random'不是线程安全的。 – dasblinkenlight 2012-02-16 11:05:12

+0

另请注意,生成真正的随机数是一件大事:http://www.random.org/randomness/ – 2012-02-16 11:13:03

+3

非常快速和线程安全:'返回4;' – 2012-02-16 11:15:24

回答

6

这是我对其采取(需要.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<double>(); 

     Parallel.ForEach(Enumerable.Range(0, 1000), i => 
     { 
      var random = RandomGenerator.GetRandomNumber(); 
      collection.Add(random); 
     }); 

     CollectionAssert.AllItemsAreUnique(collection); 
    } 
} 

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

+2

这不是线程安全的,因为它在所有线程中共享一个“Random”实例 - “seedGenerator”。迟早会发生在多线程环境中。 – LukeH 2012-02-16 12:12:59

+0

好点,我错过了seedGenerator.Next()周围的锁。我更新了这个例子。 – 2012-02-16 12:36:31

+0

当生成范围为'1 ..的数字时n',连续两次产生值'x'的几率应该是'1/n',而不是零。 – Bevan 2017-01-05 03:37:04

2

如果Random给了你相同的数字,那么你可能会错误地使用它,要么通过紧密连续创建多个实例(意味着它们将使用相同的种子并因此生成相同的序列),要么在多个线程中使用单个实例(从而“打破”该实例,因为多线程使用并不安全)。

如果速度和Random随机性在单个线程中运行时,都不够好,那么你可以尝试在ThreadLocal<T>包装,以确保在你的多线程的情况下为每个线程单独的实例:

var number = _rng.Value.NextDouble() * 100; 

// ... 

private static int _staticSeed = Environment.TickCount; 
private static readonly ThreadLocal<Random> _rng = new ThreadLocal<Random>(() => 
    { 
     int seed = Interlocked.Increment(ref _staticSeed) & 0x7FFFFFFF; 
     return new Random(seed); 
    }); 
3

我使用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); 
    }  

     /// <summary> 
     ///  Double from 0 to 1 (might be zero, will never be 1) 
     /// </summary> 
     public double GetRandomDouble() 
    {  
      return GetRandomUInt32()/(1d + UInt32.MaxValue); 
    }  

     /// <summary> 
     ///  Float from 0 to 1 (might be zero, will never be 1) 
     /// </summary> 
     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); 
    }  

    } 
}