
std::random_device 不能直接当随机数生成器用
很多人以为 std::random_device 返回的就是随机数,直接拿来调用 operator()() 就完事——这在多数平台(尤其是 Windows + MSVC 或某些旧版 libc++)上会退化成伪随机,甚至重复输出相同值。它本质是熵源,设计用途是为其他引擎“播种子”,不是用来频繁取数的。
-
std::random_device构造开销大,多次调用rd()可能阻塞或耗尽系统熵池 - Linux 上读
/dev/urandom一般没问题,但 Windows 的RtlGenRandom实现可能被绕过或缓存 - 正确做法:只调用一次或少量几次来初始化
std::mt19937等引擎
std::mt19937 是默认首选,但必须配合适的种子
使用 std::mt19937(Mersenne Twister)本身没问题,问题出在种子给得草率。用 time(nullptr) 或固定整数初始化,会导致每次运行序列完全一样,尤其在快速重启或单元测试中极易暴露。
std::random_device rd; std::mt19937 gen(rd()); // ✅ 推荐:单次取熵初始化 // std::mt19937 gen(42); // ❌ 危险:确定性种子,仅用于可复现调试
- 若需可复现(如测试),显式传入固定种子并注释清楚用途
- 生产环境避免
std::mt19937_64除非你明确需要 64 位输出且不关心初始化开销略高 - 嵌入式或无
/dev/urandom环境,考虑用硬件 RNG 或组合多个弱熵源(如时钟、内存地址、线程 ID)哈希后播种
分布对象(distribution)要复用,别每次都 new
像 std::uniform_int_distribution 或 std::normal_distribution 是无状态的轻量对象,构造开销极小,但频繁创建销毁仍会拖慢性能,尤其在 tight loop 中。
- 把分布对象声明为函数局部静态、类成员,或作用域内复用
- 不同范围的整数应使用不同分布实例,不要靠传参动态改——
std::uniform_int_distribution的a/b是构造时绑定的,不可变 - 注意
std::normal_distribution内部有缓存逻辑(Box-Muller),复用还能减少浮点运算次数
std::mt19937 gen{std::random_device{}()};
static std::uniform_int_distribution dist1{1, 6}; // 掷骰子
static std::uniform_real_distribution dist2{0.0, 1.0};
int roll = dist1(gen);
double x = dist2(gen);
多线程下别共享同一个 generator 实例
std::mt19937 和所有标准引擎都不是线程安全的——内部状态(如状态数组)被修改时无锁保护。多个线程共用一个 gen 对象,会导致生成序列错乱、重复,甚至未定义行为。
立即学习“C++免费学习笔记(深入)”;
- 每个线程持有一个独立的
std::mt19937实例(用 thread_local 或线程初始化) - 避免用全局变量或 static generator;若必须全局,用
thread_local static std::mt19937 gen{...} - 不要试图加 mutex 包裹
gen()调用——锁竞争反而比换引擎还慢,且破坏了随机性语义
thread_local static std::mt19937 gen{std::random_device{}()};
thread_local static std::uniform_int_distribution dist{0, 100};
int val = dist(gen); // ✅ 每线程隔离,无竞争
生成高质量随机数的关键不在“选多酷的算法”,而在于熵源使用克制、引擎生命周期清晰、分布复用合理、线程边界明确——这几个点一旦松动,再好的算法也白搭。










