Golang关于channel传递引用引发的坑 - Go语言中文社区

Golang关于channel传递引用引发的坑


 Time won't go back I won't turn back.

时光不会倒着走,我也不会再回头。

    其实应该理解为是slice的坑导致。 

    这个问题其实是出现在引用类型( 此处是slice )上, 这个是 slice 的数据结构,它很简单,一个指向真实 array 地址的指针 ptr ,slice 的长度 len 和容量 cap 。

30153002_0EGT.png

 结构图解1

30153002_l0gl.png

每次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("%pn", data)
	data = data[:0]
	// fmt.Printf("%pn", data)

	data = append(data, []byte("aaa")...)
	ch <- data

	time.Sleep(time.Second * 5)
}

预测的运行结果:

bbbbbbbbbb

aaa

 

但是肯定是有坑的

152649_M1Mu_2271515.png

前面新起了一个协程来等待通道接受信息, 主进程继续执行, 当data第一次传递给了通道之后, (第二次append)立刻修改了data指向数组的值(aaabbbbbbb),要注意的地方还有data = data[:0],   所以通道第一次接收的值就已经改变了, 因为我们传递的是引用,不是值类型。

如果在Append之前sleep, 那么肯定输出的是bbbbbb 。

 

解决方案呢就是加锁 或者新变量拷贝。

 

 

 

 

转载于:https://my.oschina.net/90design/blog/1615562

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_34072458/article/details/91929124
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-01 20:59:01
  • 阅读 ( 891 )
  • 分类:Go

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢