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 ...
随机推荐
- 有关linqtosql和EF的区别
LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术.他们之间的本质区别在于EF对数据库架构和我们查询的类型实行了更好的解耦.使用EF,我们查询的对象不再 ...
- R语法:<<-为全局变量赋值
例:在函数内部为全局变量赋值 all_predata_time <- data.frame(pd=0.1,Row=1,preRow=0,pt=0.1,stasid='1',InitDate='1 ...
- java 工作流项目源码 SSM 框架 Activiti-master springmvc 有手机端功能
即时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 (即时聊天功能支持手机端,详情下面有截图) 工作流模块---------------------------------- ...
- chrome浏览器使用jqprint插件打印时偶尔空白页问题
最近测试老是提bug说是有50%的概率打印出空白页,之前我也一直发现偶尔会出现这个问题,只是一直没有发现原因. 今天终于下定决心找到问题所在.开始吧! 查看源码一行行debug,发现问题只可能出现在这 ...
- PHP+Ajax+plupload无刷新上传头像代码
很简单的一款PHP+Ajax+plupload无刷新上传头像代码,兼容性很好,可以直接拿来用.你可以自定义各种类型的文件.本实例中只能上传"jpg", "png" ...
- Hadoop系列-MapReduce基础
由于在学习过程中对MapReduce有很大的困惑,所以这篇文章主要是针对MR的运行机制进行理解记录,主要结合网上几篇博客以及视频的讲解内容进行一个知识的梳理. MapReduce on Yarn运行原 ...
- 时间序列分析工具箱——tibbletime
目录 时间序列分析工具箱--tibbletime tibbletime 的用途 加载包 数据 教程:tibbletime 初始化一个 tbl_time 对象 时间序列函数 翻译自<Demo We ...
- 20155327实验一 Java开发环境的熟悉
20155327实验一 Java开发环境的熟悉 实验内容 1.使用JDK编译.运行简单的java程序 2.使用IDEA编辑.编译.运行.调试java程序 实验步骤 1.使用JDK编译.运行简单的jav ...
- .Net Core使用Unity替换原生DI
原文:.Net Core使用Unity替换原生DI 一.DIP.IOC.DI 面对对象设计原则可以帮助我们开发出更好的程序,其中有一个依赖倒置原则DIP并由此引申出IOC.DI等概念.就先粗略的了解一 ...
- PostgreSQL的hook机制初步学习
磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面:PostgreSQL内部结构与源代码研究索引页 回到顶级页面:PostgreSQL索引页 本文的目的一是为了备忘,二是为了抛砖引玉,希望 ...