Go语言使用rand.Seed()填充结构体map仅存最后元素的问题排查
异常根因定位
核心触发逻辑
- Go 全局
math/rand随机源的输出完全由种子决定:只要输入的种子值相同,后续生成的随机数序列就完全一致,按照官方设计,rand.Seed仅需在程序启动阶段调用一次,不需要每次生成随机数前重复执行。 - 三次
New方法调用在同一个goroutine内连续执行,整个流程耗时远低于系统时钟的最小精度:普通桌面/服务器操作系统的系统时间戳分辨率通常仅为1~10毫秒,三次挨在一起的函数调用取到的time.Now().UTC().UnixNano()值完全相等,相当于每次SetId都用完全一样的数值重设随机源种子。 - 每次播种完成后立刻调用
rand.Intn(100)获取id,因为种子完全相同,三次生成的随机id值完全相等。初始化map时三个条目使用同一个键,按照Go map的写入规则,重复键的后写入值会覆盖之前的值,前两个条目依次被最后一个《Watchmen》条目覆盖,最终map仅留存最后一个元素。
代码隐含设计问题(非本次故障主因)
SetId方法为值接收者,方法内对book.id的修改仅作用于接收者的临时副本,不会同步到原结构体。当前代码因为直接使用SetId的返回值初始化新结构体,暂时没有暴露这个问题,后续如果依赖方法修改原结构体的id字段,会出现难以排查的逻辑错误。
现象匹配验证
- 移除
SetId内的rand.Seed()调用后,整个程序的随机源仅使用默认初始种子,三次rand.Intn(100)调用会沿随机序列依次生成不同数值,三个id互不重复,map可以正常存储全部条目;因为没有随运行时间动态调整种子,程序每次启动生成的id序列完全固定,和实际观测到的运行表现完全吻合。
内容的提问来源于stack exchange,提问作者raizik
相关产品推荐
相关产品推荐

