2010-04-26 22 views
5

Estoy usando .NET para crear un programa de vida artificial y estoy usando la clase pseudoaleatoria de C# definida en Singleton. La idea es que si uso el mismo generador de números aleatorios en toda la aplicación, simplemente podría guardar la semilla y luego volver a cargarla desde la semilla para volver a calcular una determinada ejecución interesante.C# Generador de números aleatorios atorado en un ciclo

public sealed class RandomNumberGenerator : Random 
{ 
    private static readonly RandomNumberGenerator instance = new RandomNumberGenerator(); 

    RandomNumberGenerator() 
    { 

    } 

    public static RandomNumberGenerator Instance 
    { 
     get 
     { 
      return instance; 
     } 
    } 
} 

También quería un método que pudiera darme dos números aleatorios diferentes.

public static Tuple<int, int> TwoDifferentRandomNumbers(this Random rnd, int minValue, int maxValue) 
    { 
     if (minValue >= maxValue) 
      throw new ArgumentOutOfRangeException("maxValue", "maxValue must be greater than minValue"); 
     if (minValue + 1 == maxValue) 
      return Tuple.Create<int, int>(minValue, maxValue); 

     int rnd1 = rnd.Next(minValue, maxValue); 
     int rnd2 = rnd.Next(minValue, maxValue); 
     while (rnd1 == rnd2) 
     {     
      rnd2 = rnd.Next(minValue, maxValue); 
     } 
     return Tuple.Create<int, int>(rnd1, rnd2);    
    } 

El problema es que a veces rnd.Next(minValue,maxValue siempre devuelve minValue. Si breakpoint en este punto e intento crear un doble y configurarlo en rnd.NextDouble(), devuelve 0.0. Alguien sabe por qué está pasando esto?

Sé que es un generador de números pseudoaleatorios, pero francamente, no esperaba que se bloqueara en 0. Se está accediendo al generador de números aleatorios desde varios hilos ... ¿podría ser esta la causa del problema? ?

EDITAR: Gracias, el problema terminó siendo la seguridad del hilo.

Esta es la nueva versión de la clase.

public sealed class RandomNumberGenerator : Random 
{ 
    private static Random _global = new Random(); 
    [ThreadStatic] 
    private static Random _localInstance; 

    RandomNumberGenerator() 
    { 

    } 

    public static Random Instance 
    { 
     get 
     { 
      Random inst = _localInstance; 
      if (inst == null) 
      { 
       int seed; 
       lock (_global) seed = _global.Next(); 
       _localInstance = inst = new Random(seed); 
      } 
      return _localInstance; 
     } 
    } 
} 

Respuesta

3

Si usa solo un RNG para varios subprocesos incluso si guarda el encabezado, no podrá generar los mismos números la próxima vez que inicie su aplicación porque no estará seguro de que las llamadas al RNG de los diferentes hilos estará en el mismo orden.

Si tiene un número fijo/conocido de hilo, haga un RNG por hilo y guarde cada semilla.

Olvida lo que acabo de decir si estás 100% seguro de que cada hilo llamará al RNG exactamente con el mismo orden que la última vez si usas la misma semilla.

+0

¡Ups, había dejado esta ventana de respuesta de publicación abierta un poco demasiado, +1 ... esto es exactamente lo que quería decir! –

+0

Gracias, estabas exactamente en lo cierto. Acepté tu comentario como respuesta para darte un poco más de karma: P Además, gracias por contarme sobre el otro asunto de múltiples hilos, ¡no se me había pasado por la cabeza! –

11

La clase Random no es segura para subprocesos.

Debe realizar su instancia static[ThreadStatic], o protéjala con un candado.

+0

+1 para ['[ThreadStatic]'] (http://msdn.microsoft.com/en-us/library/system.threadstaticattribute.aspx) - nunca se supo de eso. –

1

La idea es que si utilizo el mismo generador de números aleatorios en toda la aplicación, simplemente podría guardar la semilla y luego volver a cargar desde la semilla para volver a calcular una determinada ejecución interesante.

No necesita una instancia única de RNG para esto. Si inicializa dos instancias separadas de Random en el mismo seed, producirán exactamente la misma secuencia.

Mi consejo es, guarde la semilla, pero deshágase del singleton.

1

Ni siquiera tengo que buscar la clase Random para saber "todos los métodos de instancia de esta clase o no seguro para subprocesos". Eso vale para todas las clases de .NET, con muy pocas excepciones.

Así que sí, es el multi-threading. Pero usted no ha mencionado verificar que MaxValue> MinValue tampoco.

+0

En realidad, lo compruebo, pero tienes razón ... el problema es la seguridad de las hebras. –

Cuestiones relacionadas