今天在写代码的时候遇到一个问题, 在创建一个通道的时候, 不确定创建的通道是使用 chan A 还是 chan *A.
思考了一下, 觉得这个应该和函数一样是一个值传递还是参数传递的问题. 然后写了个 play 验证了一下.
- package main
- import (
- "fmt"
- "time"
- )
- type B struct {
- Value int
- }
- type A struct {
- Bv B
- }
- func main() {
- ch := make(chan *A)
- b := B{1}
- a := A{Bv:b}
- go func(ch chan *A){
- for {
- select {
- case a := <-ch:
- a.Bv.Value = 2
- }
- }
- }(ch)
- ch <- &a
- time.Sleep(2 * time.Second)
- fmt.Println(a)
- }
这里 a.Bv.Value 的值改了. 但是如果我这里的 ch 是 make(chan A) 的话, 则打印出来的值为 1 了.
事实证明确实是这样. 再去源码里面看看.
chan 的结构是在 src/runtime/chan.go 的 hchan. 我们就看 chan.go 里面的 recv 方法
func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
这个函数就是 <-ch 的时候调用的. 这里的 c 代表的就是我们使用的这个 chan, ep 代表的是 ch 传输出来的数据存储的位置.
它在从 channel 中获取数据的时候调用的是
recv(c, sg, ep, func() { unlock(&c.lock) }, 3)
看到这个函数里面, 就可以看到使用的是 typedmemmove 这个函数, 这个函数就是 c 中的 memmove.
将原先的数据, 直接拷贝到目标内存中. 所以这里说明 channel 是进行值拷贝的.
总结
基本上, chan 的使用, 如果是结构体的话, 建议能使用指针就使用指针.
备注
本文 golang 代码基于 go1.11.4
来源: https://www.cnblogs.com/yjf512/p/10417698.html