原文链接 : 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使用不慎导致的问题的更多相关文章

  1. Golang Slice 总结

    数组 Go的切片是在数组之上的抽象数据类型,因此在了解切片之前必须要要理解数组.数组类型由指定和长度和元素类型定义.数组不需要显式的初始化:数组元素会自动初始化为零值:Go的数组是值语义.一个数组变量 ...

  2. golang slice 切片原理

    golang 中的 slice 非常强大,让数组操作非常方便高效.在开发中不定长度表示的数组全部都是 slice .但是很多同学对 slice 的模糊认识,造成认为golang中的数组是引用类型,结果 ...

  3. 解决 VS Code 中 golang.org 被墙导致的 Go 插件安装失败问题

    微软官方开发的 Go for Visual Studio Code 插件为 Go 语言 提供了丰富的支持.在 VS Code 中首次打开 Go 工作区后,VS Code 会自动检测当前开发环境为 Go ...

  4. golang slice 源码解读

    本文从源码角度学习 golang slice 的创建.扩容,深拷贝的实现. 内部数据结构 slice 仅有三个字段,其中array 是保存数据的部分,len 字段为长度,cap 为容量. type s ...

  5. Golang使用proto3协议导致零值字段不显示

    Golang使用proto3协议导致零值字段不显示 问题描述 proto协议生成的结构体如果使用直接转成json会导致零值字段不显示,这样的json是有毛病的,可以使用如下方法解决 示例Demo pa ...

  6. golang:slice陷阱

    slice陷阱,slice底层指向某个array,在赋值后容易导致array长期被引用而无法释放

  7. golang slice

    golang 在for range一个slice时,会读出其cap长度.在for的过程中,即使动态append该slice,最终for也会在第一次读取的cap长度处停止. package main i ...

  8. golang slice切片的原理以及内置函数cap, len

    golang中slice(切片)是常用的类型, slice是对数组进行封装 package main import ( "fmt" "strconv") fun ...

  9. golang——slice使用摘要

    1.slice因capacity不足而重新分配的underlying array与原本的array空间是断裂的,就是说这是原本指向的空间没变,如下 arr := [...]int{1, 2, 3, 4 ...

随机推荐

  1. 【转】Android 内核初识(6)SystemServer进程

    简介 SystemServer的进程名实际上叫做“system_server”,通常简称为SS. 系统中的服务驻留在其中,常见的比如WindowManagerServer(Wms).ActivityM ...

  2. PHP microtime() 函数

    定义和用法 microtime() 函数返回当前 Unix 时间戳和微秒数. 语法 microtime(get_as_float) 参数 描述 get_as_float 如果给出了 get_as_fl ...

  3. base大家族详解

    base大家族详解 */--> pre.src {background-color: #292b2e; color: #b2b2b2;} pre.src {background-color: # ...

  4. [luogu3943] 星空

    题面 ​ 这个题目大意上是这样的:给定一个长度为n的01串, 其中只有k个0, 每次操作时, 从给定的m种长度中选择一种, 选择序列上长度为这种的进行反转操作, 求至少需要多少次操作使得整个串全变为1 ...

  5. 多线程之Thread类

    Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知 ...

  6. SQLSERVER 数据类型int、bigint、smallint 和 tinyint范围

    [bigint] 从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数据(所有数字).存储大小为 8 个字节. [int ...

  7. Kafka设计解析(一)Kafka背景及架构介绍

    转载自 技术世界,原文链接 Kafka设计解析(一)- Kafka背景及架构介绍 本文介绍了Kafka的创建背景,设计目标,使用消息系统的优势以及目前流行的消息系统对比.并介绍了Kafka的架构,Pr ...

  8. PAT乙级1006

    1006 换个格式输出整数 (15 分)   让我们用字母 B 来表示“百”.字母 S 表示“十”,用 12...n 来表示不为零的个位数字 n(<10),换个格式来输出任一个不超过 3 位的正 ...

  9. apue2e unp安装

    最近在读 Richard Stevens 的大作<UNIX环境高级编程>,相信很多初读此书的人都会与我一样遇到这个问题,编译书中的程序实例时会出现问题,提示 “错误:apue.h:没有那个 ...

  10. expdp导出时报错ora-16000

    一.问题现象:在对数据库进行expdp导出时发生报错ora-16000,脚本如下: nohup expdp "'/ as sysdba'" schemas=shp DIRECTOR ...