Random类非线程安全,循环中new Random()因毫秒级种子重复导致相同序列;应复用单例、用Random.Shared(.NET6+)或RandomNumberGenerator加密场景。

Random 类不是线程安全的,多线程下直接复用同一个实例会产出重复值甚至抛 InvalidOperationException;别在循环里反复 new Random(),否则大概率得到一串相同数字。
为什么 new Random() 在循环里总生成一样的数?
因为 Random 默认构造函数用系统时钟(毫秒级)做种子。循环执行极快,多次调用都在同一毫秒内,种子相同 → 生成的随机序列完全一样。
- 错误写法:
for (int i = 0; i → 很可能输出五个相同的数
- 正确做法:全局或作用域内只创建一次
Random实例,重复调用Next() - 若必须在短生命周期内独立实例化(如单元测试),可显式传入不同种子,比如
new Random(Guid.NewGuid().GetHashCode())
如何生成指定范围的整数、小数、布尔值?
Random 提供了明确语义的重载方法,注意边界是「左闭右开」:
- 整数:
Next(minValue, maxValue)→ 返回 ≥minValue且的整数。例如r.Next(1, 7)等效于掷骰子(1–6) - 小数:
NextDouble()返回 [0.0, 1.0) 区间的double;要映射到 [a, b),用a + r.NextDouble() * (b - a) - 布尔值:没有内置方法,但
r.Next(2) == 0或r.NextDouble() 都可安全使用
多线程环境下怎么安全用 Random?
直接共享一个 Random 实例在并发调用 Next() 时可能出错或返回重复值,因为其内部状态更新非原子操作。
- 推荐方案:用
ThreadLocal,每个线程持有一个独占实例private static readonly ThreadLocal<random> _random = new(() => new Random());</random>
然后用_random.Value.Next(...) - 替代方案(.NET 6+):改用
Random.Shared—— 它是线程安全的静态实例,适合简单场景,但性能略低于线程本地实例 - 避免方案:用
lock包裹Next()调用 —— 锁争用高,得不偿失
Random 不适合加密或唯一 ID 场景
Random 是伪随机数生成器(PRNG),基于确定性算法,可预测、不可用于安全敏感用途。
- 需要密码学安全随机数?用
RandomNumberGenerator(如RandomNumberGenerator.GetInt32(min, max),.NET 6+)或RNGCryptoServiceProvider(旧版) - 生成唯一 ID?别用
Random拼接,直接用Guid.NewGuid()或DateTime.UtcNow.Ticks+ 自增计数器 - 模拟真实随机性(如游戏掉落)?
Random够用,但记得初始化一次、复用多次,否则概率模型就崩了
真正容易被忽略的是:Random 的种子逻辑和线程行为在不同 .NET 版本中略有差异,尤其跨平台时。如果逻辑依赖“可重现的随机序列”,务必固定种子并测试运行时环境。










