Go 1.20+ 中 math/rand 的 rand.Seed() 已弃用并移除,全局 rand.Intn() 默认返回固定值;必须显式创建独立随机源如 rand.New(rand.NewPCG(seed, stream)),且并发时需为每个 goroutine 分配独立实例。

Go 语言里用 math/rand 生成随机数,关键不是“怎么调函数”,而是“要不要调 rand.Seed()”——在 Go 1.20+ 已被弃用,硬写会报错;而没设种子或重复设种子,会导致每次运行输出完全一样。
Go 1.20+ 必须用 rand.New(rand.NewPCG()) 替代全局随机源
旧写法 rand.Seed(time.Now().UnixNano()) + rand.Intn(10) 在 Go 1.20 起编译失败,因为 rand.Seed() 已移除。新标准做法是显式创建独立的随机源:
src := rand.New(rand.NewPCG(123, 456)) // seed, stream n := src.Intn(100)
-
rand.NewPCG()比旧的rand.NewSource()更快、周期更长、统计特性更好 - 两个参数分别是初始种子和流 ID(stream),避免多个实例互相干扰
- 如果需要真随机(如密钥生成),别用
math/rand,改用crypto/rand
为什么直接用 rand.Intn() 总返回相同数字?
因为 Go 1.20+ 的全局随机源 rand.Rand 默认使用固定种子(0),所有调用都从同一状态开始:
fmt.Println(rand.Intn(10)) // 每次运行都是 5(或某个固定值) fmt.Println(rand.Intn(10)) // 还是同一个数
- 这不是 bug,是设计:保证可复现性,适合测试场景
- 若需每次不同,必须自己 new 一个带时间种子的源,例如:
rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 0)) - 注意:不要在循环里反复 new 随机源,否则纳秒级时间戳可能重复,导致种子撞车
并发环境下不能共用同一个 *rand.Rand 实例
*rand.Rand 不是并发安全的,多 goroutine 同时调 Intn() 可能 panic 或返回错误值:
- 正确做法:每个 goroutine 自己 new 一个独立实例(推荐用不同种子)
- 或者用
sync.Pool复用,但需确保pool.Get()返回后重置种子(不推荐,复杂且收益小) - 绝对不要把全局
rand包变量传进 goroutine 并发调用
真正容易出问题的,不是“不会写随机数”,而是默认行为太安静——没报错、没警告,只默默给你一串可预测的数。尤其在 CLI 工具或定时任务里,如果依赖随机打散顺序却忘了初始化自己的源,上线后就会发现“随机排序”永远是同一个顺序。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











