在 C++ 中使用 rand() 或 std::random_device 生成安全随机数

Safe random number generation using rand() or std::random_device in C++

我很难找到关于 C++ 中随机数生成的线程安全性的令人信服的资源。

我正在处理的应用程序目前只有很少的线程访问随机数生成,但将来可能会有更多。

首先使用的是:

int randomInRange(const int min, const int max)
{
    srand(time(0));
    return min + (rand() % max);
}

根据我的发现,这是错误的,因为种子是为每次调用设置的,它会对 rand() 函数的统计随机性和性能产生负面影响。

接下来尝试的是使用一个包含 std::mt19937:

的单个实例的单例
// RandomHelper.h
class RandomHelper
{
private:
    std::mt19937 engine;

    // Singleton
    RandomHelper() : engine(std::random_device()()) {}

public:
    static RandomHelper& getInstance();

    int randomInRange(int min, int max)
    {
        std::uniform_int_distribution<int> uidist(min, max);
        return uidist(engine);
    }
};

// RandomHelper.cpp
RandomHelper& RandomHelper::getInstance()
{
    static RandomHelper instance;
    return instance;
}

这应该有助于统计随机性。但是为此我明白每个线程有一个 std::mt19937 实例会更好。

我不确定我明白为什么。

这是因为如果多个线程同时调用 RandomHelper::getInstance().randomInRange(.., ..),它们会得到相同的结果吗?也就是说它不是线程安全的?

为了避免这种可能性,现在实现如下:

int randomInRange(const int min, const int max)
{
    thread_local std::mt19937 engine(std::random_device{}());
    std::uniform_int_distribution<int> uidist(min, max);
    return uidist(engine);
}

这看起来是迄今为止最好的解决方案,但我找不到关于 std::mt19937std::random_device 的线程安全的很多信息,以查看它是否真的需要 thread_local 那些与单例中的单个实例的变体。

我愿意做更多的研究,但除了通常的 C++ 参考之外,我不知道该去哪里看,据我所知,这些参考没有提到任何关于线程安全的内容。

随机数生成引擎不是线程安全的。

使用thread_local 为每个线程初始化一个引擎使其线程安全,所以我认为您的解决方案很好。

如果您对 random_device 的线程安全性有疑问,也可以 thread_local:

int randomInRange(const int min, const int max)
{
    thread_local std::random_device rd;
    thread_local std::mt19937 rng(rd());  
    thread_local std::uniform_int_distribution<int> uid;
    return uid(rng, decltype(uid)::param_type{min,max});
}

还有更多未提及的讨论话题:

C++11 Thread safety of Random number generators

我确定还有更多。