• 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传递的是内存的指针。

  • 相关阅读:
    sql server 2008收缩数据库日志
    小题大做之MySQL 5.0存储过程编程入门(收藏)
    精进不休 .NET 4.0 (5) C# 4.0 新特性之并行运算(Parallel) (收藏)
    GridView 格式化<收藏>
    MySql捕获sql语句异常的方法
    Windows7发生VS2005无法调试Web项目
    mysql 5.0存储过程学习总结《转载》
    HashMap和Hashtable及HashSet的区别
    iphone 界面实现下拉列表
    Java中堆和栈的区别
  • 原文地址:https://www.cnblogs.com/bugmaking/p/9083345.html
Copyright © 2020-2023  润新知