在 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::mt19937
或 std::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
我确定还有更多。
我很难找到关于 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::mt19937
或 std::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
我确定还有更多。