go语言 为什么slice里面的内容变了啊

go语言中的slice底层封装了数组,提供了方便的列表操作方法,是在go里面很常用的结构,但是它也有坑,踩上了,会把你折腾得怀疑人生的。不幸的是,我就被它折腾了一天,过程很让人崩溃,故记录一下,给自己提个醒,也希望能对大家有所启发。废话不多说,还是先上代码吧。

package main

import (
	"log"
	"sync"
	"sync/atomic"
)

func main() {
	row:=make([]interface{},10)//用一个slice缓存数据
	c:=make(chan []interface{},10)//数据传输channel
	go func() {//放数据线程
		for i:=0;i<10;i++{
			row1:=row[0:2]//取前两个元素作为一个新的slice
			row1[0]=i//假设往新slice里面放入数据
			c<-row1//把数据丢给channel
			//time.Sleep(time.Millisecond)
		}
		c<-[]interface{}{-1}//给个结束标志
	}()

	wg:=sync.WaitGroup{}
	wg.Add(1)
	count:=int32(0)
	go func() {//接收数据线程
		for row:=range c{
			if row[0]==-1{
				break
			}
			log.Printf("%v",row[0])
			//time.Sleep(time.Millisecond)
			atomic.AddInt32(&count,1)
		}
		wg.Done()
	}()
	wg.Wait()
	close(c)//关闭channel
	log.Printf("count->%v",count)
}

这个程序,我们设想的输出结果应该是:

2021/06/02 21:02:40 0
2021/06/02 21:02:40 1
2021/06/02 21:02:40 2
2021/06/02 21:02:40 3
2021/06/02 21:02:40 4
2021/06/02 21:02:40 5
2021/06/02 21:02:40 6
2021/06/02 21:02:40 7
2021/06/02 21:02:40 8
2021/06/02 21:02:40 9
2021/06/02 21:02:40 count->10

然而,实际输出的结果却是:

2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 9
2021/06/02 21:02:40 count->10

为啥呢,我们从0到9,一个一个数往channel里面放的,取出来也应该是0到9有序的一个数列啊,为什么全部变成9了呢?
那么究竟为什么呢,我们来仔细看看这行代码:

row1:=row[0:2]//取前两个元素作为一个新的slice

这里,我们新创建了一个slice命名为row1,我们以为它从row里面拿出前两个元素,然后新创建了一块内存区域,然后把这两个元素放到了新的内存区域;但这只是我们以为,实际上slice的底层根本就没有创建新的内存区域,它只是把row前两个元素的内存地址给了row1而已,所以channel里面的所有row1所指向的内存区域都是row前两个元素所在的区域,也正因为如此,当放数据线程最后将第一个元素的内容改成9以后,虽然接收线程接收到不同的row1,但得到的数据内容就都是9了。当然,如果接收线程接收数据比较快的话,能在放数据线程修改数据之前取到数据,就能得到正确结果。比如这样:
在这里插入图片描述
让放数据线程跑慢一点,当然这不能解决根本问题,要完全解决问题,还是得用make创建新的slice,就像这样:
在这里插入图片描述

总结:
在go里面,slice,map,interface,channel这四个结构都是引用类型,在实际使用中一定要注意数据的拷贝问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值