代码改变世界

使用chan的时候选择对象还是指针

2019-02-22 12:34 by 轩脉刃, ... 阅读, ... 评论, 收藏, 编辑

使用chan的时候选择对象还是指针

今天在写代码的时候遇到一个问题,在创建一个通道的时候,不确定创建的通道是使用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