Golang关于channel传递引用引发的坑
2018-01-30 00:00
274 查看
Time won't go back I won't turn back.
时光不会倒着走,我也不会再回头。
其实这个问题其实是出现在引用类型( 此处是slice )上, 这个是 slice 的数据结构,它很简单,一个指向真实 array 地址的指针 ptr ,slice 的长度 len 和容量 cap 。
结构图解1
每次cap改变的时候指向array内存的指针都在变化, 在实际使用中,我们最好事先预期好一个cap,这样在使用append的时候可以避免反复重新分配内存复制之前的数据,减少不必要的性能消耗。
现在上实例,来看看坑所在:
预测的运行结果:
bbbbbbbbbb
aaa
但是肯定是有坑的:
前面新起了一个协程来等待通道接受信息, 主进程继续执行, 当data传递给了通道之后, 立刻修改了data指向数组的值(第二次append), 所以通道第一次接收的值就已经改变了, 因为我们传递的是引用,不是值类型。
解决方案呢就是加锁 或者新变量拷贝。
时光不会倒着走,我也不会再回头。
其实这个问题其实是出现在引用类型( 此处是slice )上, 这个是 slice 的数据结构,它很简单,一个指向真实 array 地址的指针 ptr ,slice 的长度 len 和容量 cap 。
结构图解1
每次cap改变的时候指向array内存的指针都在变化, 在实际使用中,我们最好事先预期好一个cap,这样在使用append的时候可以避免反复重新分配内存复制之前的数据,减少不必要的性能消耗。
现在上实例,来看看坑所在:
package main import "fmt" import "time" func main() { ch := make(chan []byte, 10) go func() { for { select { case data := <-ch: fmt.Println(string(data)) } } }() data := make([]byte, 0, 32) data = append(data, []byte("bbbbbbbbbb")...) ch <- data // fmt.Printf("%p\n", data) data = data[:0] // fmt.Printf("%p\n", data) data = append(data, []byte("aaa")...) ch <- data time.Sleep(time.Second * 5) }
预测的运行结果:
bbbbbbbbbb
aaa
但是肯定是有坑的:
前面新起了一个协程来等待通道接受信息, 主进程继续执行, 当data传递给了通道之后, 立刻修改了data指向数组的值(第二次append), 所以通道第一次接收的值就已经改变了, 因为我们传递的是引用,不是值类型。
解决方案呢就是加锁 或者新变量拷贝。
相关文章推荐
- 再开一篇关于C++程序设计基核心之一:引用,指针,符号优先级,函数参数传递
- 关于python中值传递和引用传递的问题
- 转:由无名对象(临时对象)引发的关于“引用”的思考
- golang 值传递和引用传递的总结
- 关于C#值类型,引用类型,值传递,引用传递 --一个比较好的文章
- 关于按引用传递与按值传递
- 关于引用传递时,保留原地址问题
- 二叉树算法引发的指针参数传递和引用的思考
- golang 部分理解:关于channel 和 goroutine 例子
- 关于php按值传递和按引用传递问题
- 2012华为机试第三题引发的关于“java传值,传引用”思考
- 关于golang中的引用类型(reference type)
- 关于C#值类型,引用类型,值传递,引用传递
- 关于 指针的引用和指针值传递
- 关于String是值传递还是引用传递?
- 由【JAVA中参数传递问题】引发除了基本数据类型和引用类型的思考
- 关于值传递指针传递和引用传递
- C#中有关于:按 值 和 引用 传递参数 的理解
- 关于Java对象作为参数传递是传值还是传引用的问题
- 关于Java中按值传递和按引用传递的问题详解