rand.Intn 总返回相同数是因默认固定种子0,需用 rand.New 创建实例并设种子;闭区间[min,max]应写为 min + r.Intn(max-min+1),且须校验 n≠0。

rand.Intn 每次都返回相同数字,不是 bug,是设计使然:Go 1.20+ 默认用固定种子 0,全局函数不自动取系统熵——你得自己 rand.New 实例,否则“随机”就是确定性重复。
为什么 rand.Intn(10) 总是返回 5?
因为没初始化随机源。math/rand 的全局函数(如 rand.Intn、rand.Float64)在未显式构造 *rand.Rand 实例时,内部使用固定种子 0 或 1,所有调用都从同一初始状态开始。
- 漏掉
"time"导入 →time.Now().UnixNano()恒为 0 → 种子还是固定值 -
rand.Seed()在 Go 1.20+ 已彻底移除,硬写会编译失败 - 测试时反而要故意用固定种子(如
rand.NewSource(42)),确保结果可重现
怎么安全生成 [min, max] 闭区间整数?
rand.Intn(n) 只返回 [0, n),边界算错,整个逻辑就偏了。
- 错误写法:
min + rand.Intn(max)→ 实际是[min, min+max) - 错误写法:
min + rand.Intn(max-min)→ 得到的是[min, max)(左闭右开),不是闭区间 - 正确写法:
min + r.Intn(max-min+1),例如骰子:1 + r.Intn(6) -
n == 0会 panic,用户输入做参数前务必校验:if max-min+1 要提前处理
并发环境下怎么避免 panic 或竞态?
*rand.Rand 实例本身不是并发安全的。多个 goroutine 同时调 r.Intn() 可能 panic,也可能返回错误值(比如负数、超范围整数)。
- 短生命周期 goroutine(如 HTTP handler):每个都新建一个实例,用不同时间种子,简单干净
- 长周期高吞吐 worker:可用
sync.Pool复用,但Pool.New不能调time.Now(),否则失去复用意义;更推荐每个 goroutine 自己new - 别共享同一个
*rand.Rand实例而不加锁——它内部带 mutex,但频繁争抢会拖慢性能
该用 math/rand/v2 还是退回 math/rand?
看你要不要“不可预测”:
立即学习“go语言免费学习笔记(深入)”;
- 需要密码学安全(如生成 token、密钥、salt)→ 必须用
crypto/rand,别碰任何math/rand变体 - 需要真随机、零配置、线程安全、且不 care 可重现 → 用
math/rand/v2:import "math/rand/v2",然后直接rand.IntN(100) - 需要可重现(单元测试、游戏存档、压测脚本)→ 必须用
math/rand+ 手动构造*rand.Rand实例;math/rand/v2故意不支持种子 -
math/rand/v2不兼容旧代码:没有Float64()、Int31n()等方法,也不接受自定义源
math/rand 还是 crypto/rand——业务逻辑里混用或误判安全性要求,比语法错误更隐蔽也更危险。


















