golang slice使用不慎导致的问题
原文链接 : 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传递的是内存的指针。
golang slice使用不慎导致的问题的更多相关文章
- Golang Slice 总结
数组 Go的切片是在数组之上的抽象数据类型,因此在了解切片之前必须要要理解数组.数组类型由指定和长度和元素类型定义.数组不需要显式的初始化:数组元素会自动初始化为零值:Go的数组是值语义.一个数组变量 ...
- golang slice 切片原理
golang 中的 slice 非常强大,让数组操作非常方便高效.在开发中不定长度表示的数组全部都是 slice .但是很多同学对 slice 的模糊认识,造成认为golang中的数组是引用类型,结果 ...
- 解决 VS Code 中 golang.org 被墙导致的 Go 插件安装失败问题
微软官方开发的 Go for Visual Studio Code 插件为 Go 语言 提供了丰富的支持.在 VS Code 中首次打开 Go 工作区后,VS Code 会自动检测当前开发环境为 Go ...
- golang slice 源码解读
本文从源码角度学习 golang slice 的创建.扩容,深拷贝的实现. 内部数据结构 slice 仅有三个字段,其中array 是保存数据的部分,len 字段为长度,cap 为容量. type s ...
- Golang使用proto3协议导致零值字段不显示
Golang使用proto3协议导致零值字段不显示 问题描述 proto协议生成的结构体如果使用直接转成json会导致零值字段不显示,这样的json是有毛病的,可以使用如下方法解决 示例Demo pa ...
- golang:slice陷阱
slice陷阱,slice底层指向某个array,在赋值后容易导致array长期被引用而无法释放
- golang slice
golang 在for range一个slice时,会读出其cap长度.在for的过程中,即使动态append该slice,最终for也会在第一次读取的cap长度处停止. package main i ...
- golang slice切片的原理以及内置函数cap, len
golang中slice(切片)是常用的类型, slice是对数组进行封装 package main import ( "fmt" "strconv") fun ...
- golang——slice使用摘要
1.slice因capacity不足而重新分配的underlying array与原本的array空间是断裂的,就是说这是原本指向的空间没变,如下 arr := [...]int{1, 2, 3, 4 ...
随机推荐
- jQuery放大镜插件
(function($) { $.fn.magnifier = function(options){ var options = $.extend({ bigWidth: 400, //大图高度 bi ...
- Kafka设计解析(三)Kafka High Availability (下)
转载自 技术世界,原文链接 Kafka设计解析(三)- Kafka High Availability (下) 摘要 本文在上篇文章基础上,更加深入讲解了Kafka的HA机制,主要阐述了HA相关各种场 ...
- 笔记:iOS字符串的各种用法(字符串插入、字符串覆盖、字符串截取、分割字符串)(别人的代码直接复制过来的,我脸皮有点厚)
NSString* str=@"hello";//存在代码区,不可变 NSLog(@"%@",str); //1.[字符串插入] NSMutableString ...
- JQuery的ajax函数执行失败,alert函数弹框一闪而过
先查看<form>标签是否有action属性,如果没有,并且最后<button>标签的type属性为'submit‘时,默认提交位置就是当前页面 如果在页面右键检查,点击网络, ...
- 【数据结构与算法】002—树与二叉树(Python)
概念 树 树是一类重要的非线性数据结构,是以分支关系定义的层次结构 定义: 树(tree)是n(n>0)个结点的有限集T,其中: 有且仅有一个特定的结点,称为树的根(root) 当n>1时 ...
- go Context的使用
控制并发有两种经典的方式,一种是WaitGroup,另外一种就是Context WaitGroup的使用 WaitGroup可以用来控制多个goroutine同时完成 func main() { va ...
- sqlserver分区视图中分区列的规则
分区列规则 分区列存在于每个成员表上,并且通过 CHECK 约束标识特定表中的可用数据.分区列必须遵守如下规则: 每个基表都拥有键值由 CHECK 约束所强制的分区列.每个表的 CHECK 约束的键范 ...
- 大数据入门第九天——MapReduce详解(五)mapJoin、GroupingComparator与更多MR实例
一.数据倾斜分析——mapJoin 1.背景 接上一个day的Join算法,我们的解决join的方式是:在reduce端通过pid进行串接,这样的话: --order ,,P0001, ,,P0001 ...
- JavaWeb基础—HttpServletRequest
HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中, 通过这个对象提供的方法,可以获得客户端请求的所有信息. ...
- Linux下开发python django程序(Session读写)
1.登陆设置session信息 def loginsession(req): if req.method == 'POST': loginform = LoginForm(req.POST) if l ...