number - generate random values c++
Usando stdlib''s rand() de múltiples hilos (7)
Buena pregunta. No puedo responderlo directamente porque creo que hay problemas mayores. Ni siquiera parece claro que rand sea seguro para nada de todos modos. Mantiene el estado interno y no parece estar bien definido si eso es por proceso o por hilo, y si es por proceso si es seguro para subprocesos.
Para estar seguro, bloquearía un mutex alrededor de cada acceso.
O preferiblemente use una generación mejor definida como una de boost
Tengo varios hilos que ejecutan la misma función. En cada uno de estos generan un número aleatorio diferente varias veces. Intentamos hacer esto poniendo srand(time(0))
al comienzo de la función, pero parece que todos obtienen el mismo número.
¿Necesitamos llamar a srand(time(0))
solo una vez por programa, es decir, al comienzo de main
(por ejemplo), al comienzo de cada función que se llama varias veces, o alguna otra cosa?
C no fue diseñado para multihilo, por lo que el comportamiento de srand () con multihilo no está definido y depende de la biblioteca C runtime.
Muchas bibliotecas de tiempo de ejecución de Unix / Linux C usan un solo estado estático, que no es seguro para acceder desde múltiples subprocesos, por lo que con estos tiempos de ejecución C no puede usar srand () y rand () de múltiples subprocesos. Otros tiempos de ejecución de Unix C pueden comportarse de manera diferente.
El tiempo de ejecución de Visual C ++ utiliza un estado interno por subproceso, por lo que es seguro llamar a srand () para cada subproceso. Pero como señaló Neil, es probable que siembre todos los hilos con el mismo valor, así que siembre con (time + thread-id) en su lugar.
Por supuesto, para la portabilidad, use objetos Aleatorios en lugar de la función Rand, y entonces no dependería en absoluto del estado oculto. Todavía necesita un objeto por hilo, y sembrar cada objeto con (tiempo + id-hilo) sigue siendo una buena idea.
Como está utilizando C ++, en lugar de C, puede evitar los problemas de subprocesamiento a menudo asociados con srand / rand al usar c ++ 11. Esto depende de usar un compilador reciente que admita estas características. Utilizaría un motor y distribución separados en cada hilo. El ejemplo actúa como un dado.
#include <random>
#include <functional>
std::uniform_int_distribution<int> dice_distribution(1, 6);
std::mt19937 random_number_engine; // pseudorandom number generator
auto dice_roller = std::bind(dice_distribution, random_number_engine);
int random_roll = dice_roller(); // Generate one of the integers 1,2,3,4,5,6.
Me referí a Wikipedia C ++ 11 y Boost al azar al responder esta pregunta.
De la página man rand
:
La función rand () no es reentrante o thread-safe, ya que usa el estado oculto que se modifica en cada llamada.
Entonces no lo use con código enhebrado. Usa rand_r
(o drand48_r
si estás en linux / glibc). Siembre cada RNG con un valor diferente (puede sembrar un primer RNG en el hilo principal para producir semillas aleatorias para las que están en cada hilo).
Si está iniciando los hilos todo al mismo tiempo, el tiempo enviado a srand probablemente sea el mismo para cada hilo. Como todos tienen la misma semilla, todos devuelven la misma secuencia. Intente usar otra cosa, como una dirección de memoria de una variable local.
Todos obtienen el mismo número porque supuestamente comienzas todos los hilos al mismo tiempo, o todos usan la misma semilla estática, en cuyo caso estás un poco recargado. Necesitas una mejor fuente de entropía que el tiempo (). Sin embargo, un truco rápido sería sembrar con (time * thread-id) donde thread-id es el id de cada hilo de trabajo.
Por supuesto, la solución correcta en C ++ no es usar funciones de generador de números aleatorios, sino usar objetos generadores de números aleatorios, como los proporcionados por la biblioteca de números aleatorios Boost, que por su propia naturaleza (porque están basados en la pila) son hilos -seguro. Vea esta respuesta que preparé antes para un ejemplo. Sin embargo, aún puede haber un problema que proporcione suficiente entropía en un programa MT, ya que usar time () seguirá teniendo el problema que mencioné anteriormente.
srand() siembra el generador de números aleatorios. Solo debe llamar a srand(time(NULL))
una vez durante el inicio.
Dicho eso, la documentación dice:
La función
rand()
no es reentrante o thread-safe , ya que usa el estado oculto que se modifica en cada llamada. Este podría ser el valor inicial para ser utilizado por la próxima llamada, o podría ser algo más elaborado. Para obtener un comportamiento reproducible en una aplicación con hebras, este estado debe hacerse explícito. La funciónrand_r()
se suministra con un puntero a ununsigned int
, que se utilizará como estado. Esta es una cantidad muy pequeña de estado, por lo que esta función será un generador pseudoaleatorio débil. Pruebedrand48_r
(3) en su lugar.
La parte destacada de lo anterior es probablemente la razón por la cual todos sus hilos obtienen el mismo número.