Generador de números aleatorios que solo genera 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);
}
Como lo llamo:
byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);
Si paso ese bucle con el depurador durante el tiempo de ejecución, obtengo valores diferentes (que es lo que quiero). Sin embargo, si coloco un punto de interrupción dos líneas debajo de ese código, todos los miembros de la mac
matriz tienen el mismo valor.
¿Por qué sucede eso?
Cada vez que lo haces new Random()
se inicializa usando el reloj. Esto significa que en un circuito cerrado se obtiene el mismo valor muchas veces. Debes mantener una única instancia aleatoria y seguir usando Next en la misma instancia.
//Function to get a 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 comentarios): ¿por qué necesitamos un lock
aquí?
Básicamente, Next
va a cambiar el estado interno de la Random
instancia. Si hacemos eso al mismo tiempo desde varios subprocesos, se podría argumentar "acabamos de hacer que el resultado sea aún más aleatorio", pero lo que en realidad estamos haciendo es potencialmente romper la implementación interna y también podríamos comenzar a obtener los mismos números. de diferentes hilos, lo que podría ser un problema, y puede que no. Sin embargo, la garantía de lo que sucede internamente es el problema más importante; ya que Random
no ofrece ninguna garantía de seguridad de subprocesos. Por tanto, existen dos enfoques válidos:
- Sincronizar para que no accedamos al mismo tiempo desde diferentes hilos
- Utilice diferentes
Random
instancias por hilo
Cualquiera de los dos puede estar bien; pero silenciar una sola instancia de varias personas que llaman al mismo tiempo es simplemente buscar problemas.
El lock
logra 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 por subproceso, por lo que no es necesario sincronizar.
Para facilitar la reutilización en toda su aplicación, una clase estática puede resultar útil.
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; } }
}
Puede usar y luego usar una instancia aleatoria estática con código como
StaticRandom.Instance.Next(1, 100);
La solución de Mark puede resultar bastante costosa ya que necesita sincronizarse cada vez.
Podemos evitar la necesidad de sincronización utilizando el patrón de almacenamiento específico del subproceso:
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 debería ver una diferencia significativa.
Mi respuesta desde aquí :
Simplemente reiterando la solución correcta :
namespace mySpace
{
public static class Util
{
private static Random rnd = new Random();
public static int GetRandom()
{
return rnd.Next();
}
}
}
Entonces puedes llamar:
var i = Util.GetRandom();
todo a través de.
Si necesita estrictamente un método estático sin estado para generar números aleatorios, puede confiar en un archivo Guid
.
public static class Util
{
public static int GetRandom()
{
return Guid.NewGuid().GetHashCode();
}
}
Será un poquito más lento, pero puede ser mucho más aleatorio que Random.Next
, al menos según mi experiencia.
Pero no :
new Random(Guid.NewGuid().GetHashCode()).Next();
La creación innecesaria de objetos la hará más lenta, especialmente en un bucle.
Y nunca :
new Random().Next();
No solo es más lento (dentro de un bucle), su aleatoriedad es... bueno, no es muy buena en mi opinión...
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);