392 votos

Generador de números aleatorios sólo la generación de un número aleatorio

Tengo la siguiente función:

 //Function to get random number
public static int RandomNumber(int min, int max)
{
    Random random = new Random();
    return random.Next(min, max);
}
 

¿Cómo me llamo:

 byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
    mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);
 

Si me paso ese lazo con el depurador en tiempo de ejecución consigo diferentes valores (que es lo que quiero). Sin embargo, si pongo un breakpoint de dos líneas por debajo de ese código, todos los miembros de la matriz "mac" tienen el mismo valor.

¿Por qué sucede eso?

574voto

Marc Gravell Puntos 482669

Cada vez que hagas new Random() se inicializa usando el reloj. Esto significa que en un bucle estrecho, obtendrá el mismo valor un montón de veces. Usted debe tener una única Random de instancia y seguir usando Next en la misma instancia.

//Function to get random number
private static readonly Random random = new Random();
private static readonly object syncLock = new object();
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}

Editar (ver comentario): ¿por qué necesitamos un lock aquí?

Básicamente, Next va a cambiar el estado interno de la Random de instancia. Si hacemos eso, al mismo tiempo, de múltiples subprocesos, se podría argumentar que "hemos hecho el resultado aún más al azar", pero de lo que estamos realmente haciendo es potencialmente romper la implementación interna, y también podríamos empezar a sacar el mismo número de hilos diferentes, los cuales pueden ser un problema y puede que no. La garantía de lo que ocurre internamente es el mayor problema, sin embargo; desde Random ¿ no hace ninguna garantía de la seguridad de subprocesos. Por lo tanto, hay dos enfoques válidos:

  • sincronizar de manera que no podemos acceder a él al mismo tiempo desde diferentes hilos
  • el uso de diferentes Random casos por hilo

cualquiera puede ser bueno; pero la mutación de una sola instancia de varias personas que llaman al mismo tiempo, es sólo traerá problemas.

La lock alcanza el primero (y más simple) de estos enfoques; sin embargo, otro enfoque podría ser:

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

esto es, a continuación, por hilo, por lo que no necesita sincronizar.

30voto

Phil Puntos 509

Las buenas prácticas que utilice una clase auxiliar estática que se puede utilizar a través de su aplicación

 public static class StaticRandom
{
    private static int seed;

    private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    static StaticRandom()
    {
        seed = Environment.TickCount;
    }

    public static Random Instance { get { return threadLocal.Value; } }
}
 

Entonces se le puede llamar usando

 StaticRandom.Instance.Next(1, 100);
 

25voto

Hans Malherbe Puntos 1426

La solución de la marca puede ser muy caro, ya que tiene que sincronizar cada vez.

Podemos conseguir alrededor de la necesidad de sincronización utilizando el patrón de almacenamiento de hilo específico:

 
public class RandomNumber : IRandomNumber
{
    private static readonly Random Global = new Random();
    [ThreadStatic] private static Random _local;

    public int Next(int max)
    {
        var localBuffer = _local;
        if (localBuffer == null) 
        {
            int seed;
            lock(Global) seed = Global.Next();
            localBuffer = new Random(seed);
            _local = localBuffer;
        }
        return localBuffer.Next(max);
    }
}

 

Mida las dos implementaciones y usted debería ver una diferencia significativa.

11voto

fARcRY Puntos 1391

Prefiero usar la siguiente clase para generar números aleatorios:

 byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);
 

4voto

sabiland Puntos 421

1) Como Marc Gravell dijo, pruebe a utilizar UNA al azar-generador. Siempre fresco para agregar esta para el constructor: System.Environment.TickCount.

2) Una sugerencia. Digamos que usted desea crear 100 objetos y supongamos que cada uno de ellos debe tener su propio azar-generador (útil si se calculan las CARGAS de números aleatorios en un período muy corto de tiempo). Si puede hacer esto en un bucle (generación de 100 objetos), usted puede hacer esto al igual que (a asegurar totalmente la aleatoriedad):

int inMyRandSeed;

for(int i=0;i<100;i++)
{
   inMyRandSeed = System.Environment.TickCount + i;
   .
   .
   .
   myNewObject = new MyNewObject(inMyRandSeed);  
   .
   .
   .
}

// Usage: Random m_rndGen = new Random(inMyRandSeed);

Saludos.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X