golang slice使用不慎导致的问题

pert · · 933 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。
[原文链接](http://www.bugclosed.com/post/16) : http://www.bugclosed.com/post/16 ## 背景 go语言中切片slice是方便且好用的强大数据结构,但是使用的时候需要注意,不然容易出问题,最近因为遇到了一个slice的使用问题,比较典型。 有一个功能需求,用户需要获取1-20的不重复随机序列。 ## 逻辑实现 由于是需要固定的1-20共20个不同数字,所以直接定义好了唯一序列如下: ``` var( originalNumbers = []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,13,14,15,16, 17, 18, 19, 20} ) ``` 因为每个用户获取的数据的序列都需要随机打乱,实现的逻辑如下: ``` func shuffle(list []uint32) []uint32 { n := len(list) for i := n - 1; i > 0; i-- { j := dist.Int63n(int64(i + 1)) list[i], list[j] = list[j], list[i] } return list } func getOriginalNumbers() []uint32{ return originalNumbers } func GetRandomNumbers(cardType int) []uint32 { return shuffle(getOriginalNumbers()) } ``` ## 问题暴露 通过仔细分析,从以上的逻辑其实是可以发现问题的,只是写代码的时候疏忽导致没有主要到潜在问题。运行的时候发现逻辑不正确,偶尔有用户得到的序列是有重复的数字。 从原始数据的初始化来看,数字是1-20初始化到slice里面的,绝对不会出现重复。仔细看了GetRandoNumbers和shuffle打乱逻辑是存在并发访问问题的。 首先originalNumbers是一个slice,参数传递slice时仅仅是传递的切片的指针,并非复制一份切片。所以在并发的情况下,每个用户的GetRandomNumbers都会获取到同一个slice地址。而shuffle函数会对得到切片数据进行写操作(数据打乱),当出现并发写问题的时候,数据发生错乱就不足为奇了。 ## 问题解决 这个问题本质就是并发写问题,只需要将数据分离即可解决问题。 ``` func getOriginalNumbers() []uint32{ tmp := make([]uint32, len(originalNumbers)) copy(tmp, shortDeck) return tmp } ``` ## 总结 这是一个很典型的slice误用问题,slice是一个数据结构,他会指向底层真正的内存数据块,可以认为slice传递的是内存的指针。

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:701969077

933 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传