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 ...
随机推荐
- Mac环境下安装配置Hadoop伪分布式
伪分布式需要修改5个配置文件(hadoop2.x的配置文件$HADOOP_HOME/etc/hadoop) 第一个:hadoop-env.sh #vim hadoop-env.sh #第25行,由于新 ...
- C# lambda表达式参数的正确使用姿势
C#的lambda表达式的好用就不多说了,中午吃饭的时候突然想到一个以前(有年头了,难道屌丝上岁数了就回忆这个么...)和同事争执的坑.. 列个demo吧.. 先是一个类,这个类的对象就是为了吃堆内存 ...
- linux查看日志文件内容命令(面试被常问到的问题)
tail -f test.log 你会看到屏幕不断有内容被打印出来. 这时候中断第一个进程Ctrl-C, linux 如何显示一个文件的某几行(中间几行) 从第3000行开始,显示1000行.即显示3 ...
- FPGA中ROM与RAM相关知识总结(五)
把看到的关于存储的一些东西整理一下,有些话来自于网友,所以还是那句话,看到的人要带着自己的思考去看,记住尽信书不如无书,fighting!!! 一.基本概念 最熟悉的两个词语应该是RAM与ROM,RA ...
- 20155229 2016-2017-2 《Java程序设计》第九周学习总结
20155229 2016-2017-2 <Java程序设计>第九周学习总结 教材学习内容总结 第十六章 JDBC(Java DataBaseConnectivity)即java数据库连接 ...
- 19-[模块]-xml
1.xml协议 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融 ...
- 4011: [HNOI2015]落忆枫音
4011: [HNOI2015]落忆枫音 链接 分析: 原来是一个DAG,考虑如何构造树形图,显然可以给每个点找一个父节点,所以树形图的个数就是$\prod\limits_u deg[u]$. 那么加 ...
- Gitlab+Jenkins学习之路(十一)之Jenkins自动触发构建和发布
思路图: 一.下载gitlab plugin jenkins-->系统管理-->管理插件-->下载并安装gitlab plugin 二.配置gitlab认证 路径:Jenkins-- ...
- 第一章:什么是Linux
Linux可以分为四部分: Linux 内核:系统内存管理,软件程序管理,硬件设备管理和文件系统管理 GNU 工具:除了内核控制硬件系统,操作系统还需要一些工具执行标准功能,比如控制文件和程序.包括核 ...
- 团队合作开发git冲突解决方案 Intellij IDEA
https://blog.csdn.net/antony9118/article/details/52524873 注:因为我使用的是 idea 2018.1.2的版本,所以在操作上有些变化 将某些操 ...