2009-09-09 10 views
15

Ho un metodo in una classe singleton che deve utilizzare il sistema .NET. Random(), poiché il metodo viene chiamato in un ambiente multi-thread non posso crearlo solo una volta e dichiararlo staticamente, ma devo creare un oggetto Random() ogni volta che viene chiamato il metodo. Dal momento che il seme di default Random() si basa sui segni di spunta dell'orologio, non è abbastanza casuale nel mio senario. Per creare un seme migliore ho esaminato diversi metodi e ho capito che il seguente è il migliore, ma potrebbero esserci altri (più veloci/migliori) modi per farlo che mi piacerebbe sapere.Il modo migliore per seed Random() in singleton

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

risposta

23

Invece di cercare di trovare una migliore seme da soli, utilizzare System.Security.Cryptography.RandomNumberGenerator.

Utilizza un seme basato su un algoritmo complesso che coinvolge molte variabili di ambiente diverse. Il tempo di sistema è uno di quelli, come IIRC l'indirizzo MAC della scheda NIC, ecc.

È anche considerato un algoritmo "più casuale" rispetto a quello implementato dalla classe Random.

+14

chiarimento Minore: Come RandomNumberGenerator è una classe astratta, si sarebbe effettivamente utilizzare System.Security.Cryptography.RNGCryptoServiceProvider E dovrebbe essere sicuri che se il suo Singleton consente a più utenti simultanei (getInstance non è intrinsecamente sincronizzato), che protegge l'utilizzo dei membri non statici di RNGCryptoServiceProvider, poiché non sono thread-safe. – CPerkins

+1

@CPerkins: buoni punti - grazie! –

+0

@CPerkins: questo è un importante chiarimento, grazie. –

3

Creare è staticamente e utilizzare un lucchetto per rendere thread-safe:

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); 
     } 
    } 

} 

Se hai ancora bisogno di un oggetto Random in ogni thread per qualche motivo, è possibile utilizzare la classe statica di sementi di loro:

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

Utilizzando un random condiviso per seminare i randoms threadlocal, perché non ci ho pensato. L'approccio di blocco porterà ad un sacco di contese di blocco per i grandi esercizi di sgranocchiare, che sarà molto lento. – gjvdkamp

+1

Perché il downvote? Se non spieghi cosa pensi sia sbagliato, non può migliorare la risposta. – Guffa

1

programma di esempio che fa uso di RNGCryptoServiceProvider per generare numeri casuali

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

Ecco la mia implementazione. Filo sicuro senza blocco.

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); 
    } 
} 
Problemi correlati