从多个线程使用 stdlib 的 rand()

Using stdlib's rand() from multiple threads

本文关键字:rand stdlib 线程      更新时间:2023-10-16

我有几个线程,它们都运行相同的函数。在每种情况下,它们都会多次生成不同的随机数。我们试图通过将srand(time(0))放在函数的开头来实现这一点,但似乎它们都得到了相同的数字。

我们是否需要每个程序只调用srand(time(0))一次,即在main开始时(例如),在每个被调用多次的函数开始时,或者其他什么?

srand()为随机数生成器种子。您应该只需要在启动期间调用srand(time(NULL))一次。

也就是说,文件指出:

函数rand()不可重入或线程安全,因为它使用隐藏每次调用时修改的状态。这可能只是被下一个调用使用,或者它可能做一些更精细的东西。整齐在线程应用程序,此状态必须明确。函数rand_r()提供了一个指向CCD_。这是一个非常小的状态,所以这个函数将是一个弱函数伪随机生成器。尝试CCD_ 8(3)。

上面强调的部分可能是所有线程都得到相同数字的原因。

当您使用C++而不是C时,您可以通过使用C++11来避免通常与srand/rand相关的线程问题。这取决于使用支持这些功能的最新编译器。您将在每个线程上使用一个单独的引擎和分布。这个例子就像掷骰子一样。

#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 random。

来自rand手册页:

函数rand()不是可重入的,也不是线程安全的,因为它使用的是每次调用都会修改的隐藏状态。

所以不要将它与线程代码一起使用。使用rand_r(如果您在linux/glibc上,则使用drand48_r)。为每个RNG设定不同的值(可以在主线程中设定第一个RNG的种子,为每个线程中的RNG生成随机种子)。

如果同时启动所有线程,则发送到srand的时间可能与每个线程相同。由于它们都有相同的种子,所以它们都返回相同的序列。尝试使用其他东西,比如本地变量中的内存地址。

C不是为多线程设计的,因此没有定义srand()的多线程行为,并且依赖于C运行库。

许多Unix/Linux C运行库使用单一静态状态,这对于从多个线程访问是不安全的,因此使用这些C运行库时,您根本无法从多个螺纹使用srand()和rand()。其他Unix C运行时的行为可能有所不同。

Visual C++运行时使用每个线程的内部状态,因此为每个线程调用srand()是安全的。但正如Neil所指出的,您可能会为所有具有相同值的线程进行种子处理,因此使用(time+thread-id)进行种子处理。

当然,为了便于移植,可以使用Random对象而不是rand函数,这样就完全不依赖于隐藏状态。每个线程仍然需要一个对象,用(time+thread-id)为每个对象播种仍然是个好主意。

这是个好问题。我不能直接回答,因为我认为还有更大的问题。似乎还不清楚rand是否是线程安全的。它维护内部状态,如果是按进程还是按线程,如果是线程安全的,则似乎没有很好地定义。

可以肯定的是,我会在每次访问时锁定一个互斥对象。

或者最好使用定义更好的生成,例如来自boost 的生成