2011-10-17 39 views
7

Tengo varios hilos que se ejecutan simultáneamente y cada uno de ellos debe generar números aleatorios. Quiero entender si hay un patrón a seguir, para entender si es correcto inicializar el generador aleatorio con srand en el hilo principal o si cada hilo debe inicializar su propio generador aleatorio. Parece que rand/srand no ha sido diseñado para ser utilizado con hilos y me pregunto cómo puedo lidiar con hilos y números aleatorios juntos. GraciasCuál es la manera más correcta de generar números aleatorios en C con pthread

EDITAR: Necesito números aleatorios puros, pero también estoy interesado en generar una secuencia determinística para fines de prueba. Estoy en Linux, pero prefiero escribir código lo más portátil posible.

+2

¿Desea que la secuencia sea determinista o no? - http://stackoverflow.com/questions/6467585/deterministic-random-number-generator-tied-to-instance-thread-independent/6467623#6467623 – Flexo

+1

Si no tiene requisitos especiales, use 'rand_r'. –

+1

rand() es seguro para subprocesos en Linux pero no es necesario que lo sea por posix, aunque posix proporciona rand_r para ese fin. glibc usa un mutex interno para su rand() - que podría causar contención si tus hilos generan muchos números aleatorios – nos

Respuesta

7

En Linux puede usar el rand_r() para un generador mediocre o la función drand48_r() para uno mucho mejor. Ambos son reemplazos seguros para subprocesos para rand() y drand48(), al tomar un único argumento que consiste en el estado actual, en lugar de utilizar el estado global.

Con respecto a su pregunta sobre la inicialización, los dos generadores anteriores le permiten sembrar en el punto que desee, por lo que no tiene que sembrar antes de generar sus hilos.

+1

rand_r está obsoleto desde POSIX 2008. No sé por qué, pero me encantaría. –

0

En Windows puede usar la función rand_s(), que es segura para hilos. Si ya está utilizando Boost, entonces boost::random es competente (aunque aprecio que esto se etiquete con C, no con C++).

4

Cuando se trabaja con hilos y haciendo por ejemplo, simulaciones o algo así, es muy importante que tenga los generadores aleatorios independientes. En primer lugar, las dependencias entre ellos realmente pueden sesgar los resultados y, luego, los mecanismos de control de acceso al estado del generador aleatorio probablemente ralentizarán la ejecución.

En sistemas POSIX (donde usted parece ser) no es la familia de funciones, donde erand48, nrand48 y jrand48 tomar el estado del generador aleatorio como valores de entrada *rand48. Por lo tanto, puede tener fácilmente estados independientes en cada hilo. Aquellos que puede inicializar con un número conocido (por ejemplo, el número de su hilo) y tendría una secuencia reproducible de números aleatorios. O lo inicializa con algo no predecible, como la hora actual &, el número que tiene secuencias que varían para cada ejecución.

1

rand_r es seguro para subprocesos pero también es reentrante.

El siguiente código genera uint128_t números pseuso-aleatorios usando el algoritmo xorshift.

propiedades adicionales:

  • compartido reentrante
  • libre de bloqueo
  • thread-safe
  • ultrarrápidos
  • sembrado a partir de dos fuentes de variantes de entropía

uintx_types.h:

#ifndef UINTX_TYPES_H_INCLUDED 
#define UINTX_TYPES_H_INCLUDED 

#include <inttypes.h> 
#include <ctype.h> 

typedef __uint128_t  uint128_t; 
typedef __uint64_t  uint64_t; 

#define UINT128_C(hi, lo) (((uint128_t)(hi) << 64) | (uint128_t)(lo)) 
#define UINT128_MIN   UINT128_C(0x0000000000000000, 0x0000000000000000) 
#define UINT128_0   UINT128_MIN 
#define UINT128_MAX   (~(UINT128_0) - 1) 

#endif // UINTX_TYPES_H_INCLUDED 

lf.h:

#ifndef LF_H_INCLUDED 
#define LF_H_INCLUDED 

#define AAF(ADDR, VAL)   __sync_add_and_fetch((ADDR), (VAL)) 

#endif // LF_H_INCLUDED 

rand.h:

#ifndef RAND_H_INCLUDED 
#define RAND_H_INCLUDED 

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <time.h> 
#include <limits.h> 
#include <fcntl.h> 
#include <sys/types.h> 
#include <unistd.h> 

#include "lf.h" 
#include "uintx_types.h" 


#define URANDOM  "/dev/random" 

void  srand_init(void); 
uint128_t rand_range_128(uint128_t min, uint128_t max); 

#endif // RAND_H_INCLUDED 

rand.c:

#include "rand.h" 

uint64_t r[2]; 

uint64_t xorshift64star(int index) 
{ 
    uint64_t x; 

    x = r[index]; 
    x ^= x >> 12; // a 
    x ^= x << 25; // b 
    x ^= x >> 27; // c 
    x = x * UINT64_C(2685821657736338717); 
    return AAF(&r[index], x); 
} 

void srand_init(void) 
{ 
    struct timespec ts; 
    size_t   nbytes; 
    ssize_t   bytes_read; 
    int    fd; 

    clock_gettime(CLOCK_REALTIME, &ts); 
    r[0] = (uint64_t)(ts.tv_sec * 1.0e9 + ts.tv_nsec); 
    xorshift64star(0); 

    if ((fd = open(URANDOM, O_RDONLY, S_IRUSR | S_IRGRP | S_IROTH)) == -1) 
    { 
     r[1] = r[0] + 1; 
     xorshift64star(1); 
    } 
    else 
    { 
     nbytes = sizeof(r[1]); 
     bytes_read = read(fd, &r[1], nbytes); 
     if ((bytes_read == 0) || (r[1] == 0ull)) 
     { 
      r[1] = r[0] + 1; 
      xorshift64star(1); 
     } 
     close(fd); 
    } 
} 

uint64_t rand_64(void) 
{ 
    return xorshift64star(0); 
} 

uint128_t rand_128(void) 
{ 
    uint128_t  r; 

    r = xorshift64star(0); 
    r = (r << 64) | xorshift64star(1); 
    return r; 
} 


uint128_t rand_range_128(uint128_t min, uint128_t max) 
{ 
    return (rand_128() % (max+1-min))+min; 
} 

test.c:

#define KEYS 1000 

int main(int argc, char **argv) 
{ 
    int    i; 
    uint128_t  key; 

    srand_init(); 

    for(i = 0; i <= KEYS; i++) 
    { 
     key = rand_range_128(UINT128_MIN, UINT128_MAX); 
     printf("%016"PRIx64"%016"PRIx64"\n", (uint64_t)(key >> 64), (uint64_t)key); 

    } 
    return 0; 
} 

Compilar con gcc (4.9.2) en Linux.

+0

Perdón por el "necroposteo" aquí, pero quería preguntar si podía modificar srand_init (void) en srand_init (unsigned int * seed) y en vez de clock_gettime (CLOCK_REALTIME, &ts); r [0] = (uint64_t) (ts .tv_sec * 1.0e9 + ts.tv_nsec); simplemente ponga r [0] = uint64_t (* seed), por lo que siempre puedo elegir cómo inicializar mi rng. –

+0

Además, ¿cómo debo inicializar este rng de una manera segura para los hilos? ¿Engendro mis hilos y luego cambio la semilla y uso srand_init (semilla) en cada hilo? O ¿puedo llamar a rand_range_128 (UINT128_MIN, UINT128_MAX) de forma segura dentro de mi región paralelizada? –

0

En los sistemas Linux se puede utilizar una función como:

size_t random_between_range(size_t min, size_t max){ 
    unsigned short state[3]; 
    unsigned int seed = time(NULL) + (unsigned int) pthread_self(); 
    memcpy(state, &seed, sizeof(seed)); 
    return min + nrand48(state) % (max - min); 
} 

Aquí tengo que decir que ¡No sé realmente si los números generados por esta función ajuste a la distribución normal en otras palabras, si esta función es un RNG válido en el rango (mínimo, máximo), pero al menos me ayudó a escribir un punto de referencia simple que requería algunos números aleatorios.

Como puede ver, la función utiliza la identificación de subprocesos POSIX para reorganizar la semilla aleatoria. Al hacerlo, cada subproceso tiene su propia semilla aleatoria en lugar de usar estado global según time(NULL)

Cuestiones relacionadas