2009-09-09 16 views
15

Tengo un método en una clase singleton que necesita usar el sistema .NET. Random(), dado que se llama al método en un entorno de subprocesos múltiples, no puedo crearlo solo una vez y declararlo estáticamente, pero tengo que crear un objeto Random() cada vez que se llama al método. Dado que Random() inicialización predeterminada se basa en las marcas de reloj, no es lo suficientemente aleatorio en mi senario. Para crear una mejor semilla, he examinado varios métodos y he pensado que el siguiente es el mejor, pero puede que haya otras formas (más rápidas/mejores) de hacerlo que me gustaría saber.La mejor manera de inicializar Random() en singleton

Random rnd = new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)); 

Respuesta

23

En lugar de tratar de llegar a una mejor sembrar mismo, utilizar System.Security.Cryptography.RandomNumberGenerator.

Utiliza una semilla basada en un complejo algoritmo que involucra muchas variables de entorno diferentes. La hora del sistema es una de ellas, al igual que IIRC la dirección MAC de su NIC, etc.

También se considera un algoritmo 'más aleatorio' que el implementado por la clase Aleatoria.

+14

aclaración Menor: Como RandomNumberGenerator es una clase abstracta, él realmente utilizan System.Security.Cryptography.RNGCryptoServiceProvider Y él debe estar seguro de que si su producto único permite que varios usuarios simultáneos (getInstance no está sincronizado inherentemente), que protege el uso de los miembros no estáticos de RNGCryptoServiceProvider, ya que no son seguros para subprocesos. – CPerkins

+1

@CPerkins: Buenos puntos, ¡gracias! –

+0

@CPerkins: Esa es una gran aclaración, gracias. –

3

Crear estáticamente y utilizar un bloqueo para que sea hilo de seguridad:

public static class RandomProvider { 

    private static Random _rnd = new Random(); 
    private static object _sync = new object(); 

    public static int Next() { 
     lock (_sync) { 
     return _rnd.Next(); 
     } 
    } 

    public static int Next(int max) { 
     lock (_sync) { 
     return _rnd.Next(max); 
     } 
    } 

    public static int Next(int min, int max) { 
     lock (_sync) { 
     return _rnd.Next(min, max); 
     } 
    } 

} 

Si aún necesita un objeto Random en cada hilo por alguna razón, se puede utilizar la clase estática sembrarlos:

Random r = new Random(RandomProvider.Next()^Environment.TickCount); 
+1

Usando un azar compartido para sembrar los randoms locales, ¿por qué no pensé en eso? Sin embargo, el enfoque de bloqueo dará lugar a una gran cantidad de bloqueos para grandes ejercicios de crujido, que será muy lento. – gjvdkamp

+1

¿Por qué el voto a favor? Si no explica lo que piensa que está mal, no puede mejorar la respuesta. – Guffa

1

programa de ejemplo que hace uso de RNGCryptoServiceProvider para generar números aleatorios

class Program 
{ 

    public static void Main(string[] args) 
    { 
     int i; 
     byte bRandom; 
     String sL; 
     for (i = 0; i < 10; i++) 
     { 
      bRandom = GetRandom(); 
      sL = string.Format("Random Number: {0}", bRandom); 
      Console.WriteLine(sL); 
     } 
     Console.ReadLine(); 
    } 

    public static byte GetRandom() 
    { 
     RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider(); 
     // Create a byte array to hold the random value. 
     byte[] randomNumber = new byte[1]; 

     rngCsp.GetBytes(randomNumber); 

     return randomNumber[0]; 

    } 
} 
11

Aquí está mi implementación. Thread-safe sin bloqueo.

public static class StrongRandom 
{ 
    [ThreadStatic] 
    private static Random _random; 

    public static int Next(int inclusiveLowerBound, int inclusiveUpperBound) 
    { 
     if (_random == null) 
     { 
      var cryptoResult = new byte[4]; 
      new RNGCryptoServiceProvider().GetBytes(cryptoResult); 

      int seed = BitConverter.ToInt32(cryptoResult, 0); 

      _random = new Random(seed); 
     } 

     // upper bound of Random.Next is exclusive 
     int exclusiveUpperBound = inclusiveUpperBound + 1; 
     return _random.Next(inclusiveLowerBound, exclusiveUpperBound); 
    } 
} 
Cuestiones relacionadas