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

90design · · 610 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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

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

 其实这个问题其实是出现在引用类型( 此处是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), 所以通道第一次接收的值就已经改变了, 因为我们传递的是引用,不是值类型。

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

 

 

 

 

本文来自:开源中国博客

感谢作者:90design

查看原文:Golang关于channel传递引用引发的坑

入群交流(和以上内容无关):Go中文网 QQ 交流群:798786647 或加微信入微信群:274768166 备注:入群;关注公众号:Go语言中文网

610 次点击  
加入收藏 微博
4 回复  |  直到 2018-07-24 15:57:05
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传