math/rand默认种子为1导致每次运行随机数相同,需用time.Now().unixNano()设种;生成[1,100]整数应写为1+r.Intn(100);并发下应避免全局函数,推荐局部*rand.Rand实例或sync.Pool复用。

为什么 math/rand 生成的随机数每次运行都一样?
因为默认种子(seed)固定为 1,rand.NewSource(1) 导致序列完全可复现。这不是 bug,是设计使然——方便测试,但生产环境必须手动设种。
正确做法是用当前时间纳秒级精度初始化:
src := rand.NewSource(time.Now().UnixNano()) r := rand.New(src)
注意别在循环里反复调用 time.Now() 初始化,否则高并发下可能撞上相同种子;全局复用一个 *rand.Rand 实例更安全。
如何生成指定范围的随机整数(比如 [1, 100])?
rand.Intn(n) 返回 [0, n) 的整数,不能直接套用。要得到闭区间 [min, max],得做偏移和长度校准:
立即学习“go语言免费学习笔记(深入)”;
- 先算长度:
max - min + 1 - 再加偏移:
min + r.Intn(max-min+1)
例如生成 1 到 100(含):
num := 1 + r.Intn(100) // ✅ 不是 r.Intn(100) + 1 写法错误,但语义等价;重点是范围计算别漏 +1
常见错误:写成 r.Intn(100) + 1 看似对,但如果 min 是变量,漏掉 +1 就变成 [min, max) 了。
如何生成随机浮点数或字符串?
math/rand 提供 float32() 和 Float64(),返回 [0.0, 1.0) 区间值,缩放即可:
// [5.0, 15.0) f := 5.0 + r.Float64()*10.0
生成随机字符串需组合使用 r.Intn() 和字符集:
const letters = "abcdefghijklmnopqrstuvwxyz0123456789" b := make([]byte, 8) for i := range b { b[i] = letters[r.Intn(len(letters))] } s := string(b)
注意:不要用 rand.Read() 混淆——那是 crypto/rand 的函数,用于密码学安全场景,性能差、阻塞式,math/rand 不提供该方法。
并发环境下直接用全局 rand.* 函数安全吗?
不安全。rand.Intn() 等全局函数操作的是包级全局变量 rand.Rand,内部用 mutex 保护,但会成为性能瓶颈,且 go 1.20+ 已标记为「不推荐」。
推荐方式是每个 goroutine 自持实例,或用 sync.Pool 复用:
var randPool = sync.Pool{ New: func() interface{} { return rand.New(rand.NewSource(time.Now().UnixNano())) }, }
然后在 goroutine 中取用:r := randPool.Get().(*rand.Rand),用完记得 randPool.Put(r)。不过多数服务中,直接用带 seed 的局部 *rand.Rand 更清晰可控。
真正容易被忽略的是:哪怕你用了 sync.Pool,也别把 time.Now().UnixNano() 放在 New 函数里——池中对象可能复用,重复设种反而导致随机性下降。应该在每次 Get 后重置 seed,或干脆只用一次性的实例。