从多个线程使用stdlib的rand()
我有几个线程都运行相同的function。 在每一个这些他们产生一个不同的随机数数倍。 我们试图通过把srand(time(0))
放在srand(time(0))
的开头来做到这一点,但似乎它们都得到了相同的数字。
我们是否需要每个程序只调用一次srand(time(0))
,即在main
(例如)的开始处,每次调用几次函数的开始处,还是别的东西?
srand()给随机数发生器播种。 你只需要在启动srand(time(NULL))
调用srand(time(NULL))
一次。
这就是说,文件指出:
函数
rand()
不可重入或线程安全 ,因为它使用每次调用时都修改的隐藏状态。 这可能只是下一次通话使用的种子价值,也可能是更精细的东西。 为了在线程应用程序中获得可重复的行为,必须明确这个状态。 函数rand_r()
提供了一个指向unsigned int
的指针,用作状态。 这是一个非常小的状态,所以这个函数将是一个弱的伪随机生成器。 试试drand48_r
(3)。
上面强调的部分可能就是你的所有线程获得相同数字的原因。
如果您同时启动所有线程,发送到srand的时间可能与每个线程相同。 由于他们都有相同的种子,他们都返回相同的顺序。 尝试使用其他的东西,像一个局部variables的内存地址。
从rand
手册页:
函数rand()不可重入或线程安全,因为它使用每次调用时都修改的隐藏状态。
所以不要使用线程代码。 使用rand_r
(或者如果你在linux / glibc上,则使用rand_r
)。 为每个RNG分配一个不同的值(可以在主线程中产生一个第一个RNG,为每个线程产生随机种子)。
正如你使用C ++而不是C,你可以通过使用c ++ 11来避免与srand / rand有关的线程问题。 这取决于使用支持这些function的最新编译器。 您将在每个线程上使用单独的引擎和分配。 这个例子就像一个骰子。
#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.
在回答这个问题时,我提到了维基百科的C ++ 11和Boost 。
这是一个很好的问题。 我不能直接回答,因为我觉得有更大的问题。 无论如何,甚至似乎都不清楚rand是否是线程安全的。 它维护内部状态,如果它是每个进程或每个线程,并且它是每个进程,如果它是线程安全的,它似乎没有被很好地定义。
确信我会locking每个访问周围的互斥锁。
或者最好使用一个更好的定义的生成,例如一个来自boost
C不是为multithreadingdevise的,所以srand()与multithreading的行为没有定义,并且取决于C运行时库。
许多Unix / Linux C运行时库使用单一静态状态,这对multithreading访问是不安全的,所以对于这些C运行时,根本不能使用multithreading中的srand()和rand()。 其他的Unix C运行时可能会有不同的performance。
Visual C ++运行时使用每个线程的内部状态,因此为每个线程调用srand()是安全的。 但是正如Neil所指出的那样,您可能会将所有线程都设为相同的值 – 所以使用(time + thread-id)来代替。
当然,为了可移植性,使用Random对象而不是rand函数,然后根本不依赖于隐藏状态。 每个线程仍然需要一个对象,并且使用(time + thread-id)为每个对象播种仍然是一个好主意。
他们都得到相同的数字,因为你可能会同时开始所有的线程,或者他们都使用相同的静态种子,在这种情况下,你有点塞满了。 你需要一个比时间更好的熵源()。 然而,一个快速的黑客将是种子(时间*线程ID)其中线程ID是每个工作线程的ID。
当然,在C ++中正确的解决scheme是不使用随机数生成器函数,而是使用随机数生成器对象,如由Boost随机数库提供的,它们本质上(因为它们是基于堆栈的)是线程-安全。 看到我早些时候做的一个例子。 然而,在MT程序中提供足够的熵可能仍然存在问题,因为使用时间()仍然会有我上面提到的问题。