使用pthread在C中生成随机数的最正确方法是什么

我有几个并发运行的线程,每个线程都必须生成随机数。 我想了解是否存在要遵循的模式,以了解在主线程中使用srand初始化随机生成器是否正确,或者每个线程是否必须初始化其自己的随机生成器。 似乎rand / srand没有被设计为与线程一起使用,我想知道如何处理线程和随机数。 谢谢

编辑:我需要纯随机数,但我也有兴趣为测试目的生成确定性序列。 我在Linux上,但我更喜欢尽可能地编写代码。

在Linux上,您可以将rand_r()用于普通生成器或drand48_r()函数以获得更好的生成器。 两者都是rand()drand48()线程安全替换,通过采用由当前状态组成的单个参数,而不是使用全局状态。

关于初始化的问题,上面的两个生成器都允许你在你想要的任何点播种,所以在产生你的线程之前你不会被迫播种它们。

当使用线程并进行例如模拟时,将随机生成器独立是非常重要的。 首先,它们之间的依赖关系可能会对结果产生偏差,然后对随机生成器状态的访问控制机制很可能会降低执行速度。

在POSIX系统(你似乎是)上有*rand48系列函数,其中erand48nrand48jrand48将随机生成器的状态作为输入值。 因此,您可以在每个线程中轻松拥有独立状态。 那些你可以使用已知数字(例如你的线程数)进行初始化,并且你有一个可重现的随机数序列。 或者,您可以使用不可预测的内容对其进行初始化,例如当前时间和数字,以使序列在每次执行时都有所不同。

在Windows上,您可以使用rand_s()函数,该函数是线程安全的。 如果你已经在使用Boost,那么boost :: random是胜任的(虽然我很欣赏这是标记C,而不是C ++)。

rand_r是线程安全的,但也是可重入的。

下面的代码使用xorshift算法生成uint128_t pseuso-random数字。

附加属性:

  • 共享重入
  • 无锁
  • 线程安全
  • 超快
  • 来自两个变异的enthropy来源

uintx_types.h:

 #ifndef UINTX_TYPES_H_INCLUDED #define UINTX_TYPES_H_INCLUDED #include  #include  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  #include  #include  #include  #include  #include  #include  #include  #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; } 

在Linux下用gcc(4.9.2)编译。

这个链接提供了一些从/ dev / random读取的代码 – 这应该是线程安全的 – 问题是同样的:

读/ dev / urandom线程安全吗?

在Linux系统上,您可以使用以下function:

 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 ); } 

在这里我不得不说,我不知道这个函数生成的数字是否适合正态分布,换句话说,如果这个函数是范围内的有效RNG(min,max),但至少对我来说写一个简单的需要一些随机数的基准。

如您所见,该函数利用POSIX线程ID来重新排列随机种子。 这样做,每个线程都有自己的随机种子,而不是使用全局状态,具体取决于time(NULL)