2010-10-19 18 views
21

Estoy tratando de aprender a usar rand_r, y después de leer this question todavía estoy un poco confundido, ¿alguien puede echar un vistazo y señalar lo que me estoy perdiendo? A mi entender, rand_r toma un puntero a algún valor (o una pieza de memoria con algún valor inicial) y lo usa para generar nuevos números cada vez que se lo llama. Cada hilo que llame a rand_r debe proporcionarle un puntero único (o pieza de memoria) para obtener números "aleatorios reales" entre diferentes hilos. Es por eso que esto:¿Cómo uso rand_r y cómo lo uso de una manera segura para subprocesos?

int globalSeed; 

//thread 1 
rand_r(&globalSeed); 

//thread 2 
rand_r(&globalSeed); 

es la forma incorrecta de usarlo. Si tengo

int seed1,seed2; 

//thread 1 
rand_r(&seed1); 

//thread 2 
rand_r(&seed2); 

, ¿esta sería la forma correcta de generar números "aleatorios verdaderos" entre los hilos?


EDIT: preguntas adicionales después de leer las respuestas a la parte de arriba:

  1. si en el hilo 1 Necesito un azar número entre 1 y n, debo hacer (rand_r(&seed1) % (n-1)) + 1? ¿O hay otra forma común de hacer esto?
  2. ¿Es correcto o normal si la memoria de la semilla se asigna dinámicamente?

Respuesta

15

Correcto. Lo que está haciendo en el primer caso es eludir la naturaleza de seguridad de subprocesos de rand_r. Con muchas funciones no seguras para subprocesos, el estado persistente se almacena entre las llamadas a esa función (como la semilla aleatoria aquí).

Con la variante de seguridad de subprocesos, realmente proporciona datos de subproceso específicos (seed1 y seed2) para garantizar que el estado no se comparte entre subprocesos.

Tenga en cuenta que esto no hace que los números sean verdaderamente aleatorios, simplemente hace que las secuencias sean independientes entre sí. Si los inicia con la misma semilla, probablemente obtendrá la misma secuencia en ambos hilos.

Por ejemplo, supongamos que obtiene una secuencia aleatoria 2, 3, 5, 7, 11, 13, 17 con una inicial semilla de 0. Con una semilla compartida, alternar llamadas a rand_r desde dos subprocesos diferentes causar esto:

thread 1    thread 2 
      <--- 2 
       3 ---> 
      <--- 5 
       7 ---> 
      <--- 11 
       13 ---> 
      <--- 17 

y ese es el mejor de los casos - en realidad se puede encontrar que el estado compartido se corrompe ya que los cambios en él pueden no ser atómica.

Con el estado no compartido (con a y b que representa las dos fuentes distintas de los números aleatorios):

thread 1    thread 2 
      <--- 2a 
       2b ---> 
      <--- 3a 
       3b ---> 
      <--- 5a 
       5b ---> 
       :: 

Algunas llamadas seguras para subprocesos que requieren para proporcionar el estado-hilo específico como éste, otros puede crear datos específicos de subprocesos bajo las carátulas (usando una ID de subproceso o información similar) para que nunca tenga que preocuparse por ello, y puede usar exactamente el mismo código fuente en entornos con subprocesos y sin subprocesos. Yo prefiero este último, simplemente porque me hace la vida más fácil.


material adicional para la pregunta Editado:

> If in thread 1, I need a random number between 1 to n, should I do '(rand_r(&seed1) % (n-1)) + 1', or there is other common way of doing this?

Suponiendo que desea un valor entre 1 y nincluido, utilice (rand_r(&seed1) % n) + 1. El primer bit le da un valor de 0 a n-1 inclusive, luego agrega 1 para obtener el rango deseado.

> Is it right or normal if the memory for the seed is dynamically allocated?

La semilla tiene que ser persistente, siempre y cuando usted lo está utilizando. Podría asignarlo dinámicamente en la secuencia, pero también podría declararlo en la función de nivel superior de la secuencia. En ambos casos, tendrá que comunicar la dirección a los niveles más bajos de alguna manera (a menos que su hilo sea solo esa función que es poco probable).

Puede pasarlo a través de las llamadas de función o configurar una matriz global de alguna manera donde los niveles inferiores pueden descubrir la dirección de inicialización correcta.

Como alternativa, ya que necesita una matriz global de todos modos, puede tener una matriz global de semillas en lugar de direcciones semilla, que los niveles inferiores podrían usar para descubrir su semilla.

Probablemente (en ambos casos de usar la matriz global) tenga una estructura con clave que contenga la ID de la cadena como la clave y la semilla que se utilizará. Luego tendría que escribir su propia rutinarand() que localizó la semilla correcta y llamó al rand_r() con eso.

Esto es por eso que prefiero las rutinas de la biblioteca que hacen esto debajo de las coberturas con datos específicos de subprocesos.

+0

gracias! entonces, la mejor manera es usar diferentes semillas con diferentes valores iniciales para diferentes hilos, ¿verdad? – derrdji

+0

Yo diría que generalmente, sí. Pero hay algunas situaciones en las que es posible que desee un estado compartido (primer diagrama en mi respuesta), en cuyo caso podría evitar la posibilidad de corrupción llamando 'rand_r' al _same_ pero protegido por un mutex. Lo único que no quiere hacer es inicializar ambas secuencias con la misma semilla, ya que las secuencias serían idénticas. – paxdiablo

+0

sí, ¡gracias! Estaba a punto de preguntar si podía compartir uno y guardar la llamada rand_r con un bloqueo mutex. – derrdji

Cuestiones relacionadas