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 ...
随机推荐
- 【转】Android 内核初识(6)SystemServer进程
简介 SystemServer的进程名实际上叫做“system_server”,通常简称为SS. 系统中的服务驻留在其中,常见的比如WindowManagerServer(Wms).ActivityM ...
- PHP microtime() 函数
定义和用法 microtime() 函数返回当前 Unix 时间戳和微秒数. 语法 microtime(get_as_float) 参数 描述 get_as_float 如果给出了 get_as_fl ...
- base大家族详解
base大家族详解 */--> pre.src {background-color: #292b2e; color: #b2b2b2;} pre.src {background-color: # ...
- [luogu3943] 星空
题面 这个题目大意上是这样的:给定一个长度为n的01串, 其中只有k个0, 每次操作时, 从给定的m种长度中选择一种, 选择序列上长度为这种的进行反转操作, 求至少需要多少次操作使得整个串全变为1 ...
- 多线程之Thread类
Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知 ...
- SQLSERVER 数据类型int、bigint、smallint 和 tinyint范围
[bigint] 从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数据(所有数字).存储大小为 8 个字节. [int ...
- Kafka设计解析(一)Kafka背景及架构介绍
转载自 技术世界,原文链接 Kafka设计解析(一)- Kafka背景及架构介绍 本文介绍了Kafka的创建背景,设计目标,使用消息系统的优势以及目前流行的消息系统对比.并介绍了Kafka的架构,Pr ...
- PAT乙级1006
1006 换个格式输出整数 (15 分) 让我们用字母 B 来表示“百”.字母 S 表示“十”,用 12...n 来表示不为零的个位数字 n(<10),换个格式来输出任一个不超过 3 位的正 ...
- apue2e unp安装
最近在读 Richard Stevens 的大作<UNIX环境高级编程>,相信很多初读此书的人都会与我一样遇到这个问题,编译书中的程序实例时会出现问题,提示 “错误:apue.h:没有那个 ...
- expdp导出时报错ora-16000
一.问题现象:在对数据库进行expdp导出时发生报错ora-16000,脚本如下: nohup expdp "'/ as sysdba'" schemas=shp DIRECTOR ...