golang 常見疑惑總結

weixin_34119545發表於2018-05-04

  經常會有一些朋友問go語言的一些問題和疑惑,其實好多問題在官方文件和stackoverflow裡都有詳細的講解,只要你肯花時間讀一遍官方文件Effective Go基本上都有找到答案。本文總結一下大家經常問到的一些問題,長期更新。

  程式碼都在github上, 地址 https://github.com/lpxxn/gocommonquestions

 

new 和make 的區別

  簡單來說,new(T)用於分配記憶體,返回指向T型別的一個指標,指標的值為T型別的零值

    n1 := new(int)
    fmt.Println(n1) // console the address of n1
    fmt.Println(*n1 == 0) // zero value

    type T struct {
        I int
        A string
        Next *T
    }
    n2 := new(T)
    fmt.Println(n2)
    fmt.Println(n2.I == 0)
    fmt.Println(n2.Next == nil)
    fmt.Println(n2.A == "")

    n3 := new([]int) 
    fmt.Println(n3)
    fmt.Println(*n3 == nil)

 

  make(T)   只能用於slice、map和channel, 返回非零值的T。

    m1 := make([]int, 1)
    fmt.Println(m1)
    m2 := make(map[int]string)
    m2[0] = "abcde"
    m3 := make(chan int)

    m4 := make(chan int, 5)

  make 返回的是型別本身,new 返回的是指向型別的指標

 

相關講解

https://stackoverflow.com/questions/9320862/why-would-i-make-or-new

https://golang.org/doc/effective_go.html#allocation_new

https://golang.org/ref/spec#The_zero_value

 

是否需要主動關閉channel

  除非你的程式需要等待channel關閉後做一些操作,不用主動去關閉channel。當沒有地方在使用這個channel的時候go的垃圾回收系統會自動回收,如果channel裡還有值,但是沒有地方引用了,也會被回收。

  下面的小例子就是在等待channel c1和c2關閉後,再做一些事情。  

func main() {
    c1 := make(chan int, 3)
    go test1(c1)
    for v := range c1 {
        fmt.Println(v)
    }

    fmt.Println("after close c1 do something")
    c2 := make(chan bool)
    go func() {
        time.AfterFunc(time.Second * 3, func() {
            close(c2)
        })
    }()
    _, close := <- c2
    if !close {
        fmt.Println("after c2 closed do something")
    }

    fmt.Println("end")
}

func test1(c chan<- int) {
    for i := 0; i < 5; i++ {
        c <- i
    }
    close(c)
}

簡單測試垃圾回收channel

func createChan() chan int64{
    c := make(chan int64, 10000)
    c <- 1
    return c
}


func main() {

    RM()
    for i := 0; i < 10; i++ {
        c := createChan()
        c <- 2
        RM()
        runtime.GC()
    }
    RM()
}

func RM(){
    var m runtime.MemStats
    runtime.ReadMemStats(&m)
    fmt.Println(m.Alloc)
}

  

相關講解:https://stackoverflow.com/questions/8593645/is-it-ok-to-leave-a-channel-open

 https://groups.google.com/forum/#!msg/golang-nuts/pZwdYRGxCIk/qpbHxRRPJdUJ

https://groups.google.com/forum/#!topic/golang-nuts/KtIyc5lTRJY

Unbuffered channel和buffered channel 區別

buffered channel

c3 := make(chan bool, 5)  // buffered channel

buffered channel 可以持續的傳送資料到channel,直到channel滿為至。不用等待是否有接收channel。

如果channel滿了,會等待讀取channel,當有channel被讀取,就會繼續傳送資料到channel

    c3 := make(chan bool, 5)  // buffered channel

    go func() {
        for i := 0; i < 20; i++ {
            c3 <- i % 2 == 0
        }
        close(c3)
    }()

    for v := range c3 {
        fmt.Println(v)
    }

 

 unbuffered channel

下面這兩種宣告是一樣的

    c1 := make(chan bool, 0)  // unbuffered channel
    c2 := make(chan bool)     // unbuffered channel

  unbuffered channel  的接收channel會一直阻塞,直到有值傳給channel, 也可以說傳送channel會一直阻塞,至到有接收channel

    c1 := make(chan bool, 0)  // unbuffered channel
    c2 := make(chan bool)     // unbuffered channel

    go func() {
        c1 <- false
        time.Sleep(time.Second * 2)
        c2 <- true
    }()

    fmt.Println(<-c1)
    fmt.Println(<-c2)

相關講解:

https://stackoverflow.com/questions/23233381/whats-the-difference-between-c-makechan-int-and-c-makechan-int-1

https://golang.org/doc/effective_go.html

 

定義型別和組合型別的區別

  定義型別,也可以說是別名和組合型別的區別

    有一個Test的結構,NewTest是以Test為型別的一個定義,New2Test和New3Test都是組合型別

type Test struct         { N int }
func (m *Test) Name()    { fmt.Println("abc")}


// NewTest does not inherit any functions of Test
// can access fields
type NewTest Test


// New2Test is composite type, it inherit all functions of Test
// can access fields
type New2Test struct {
    Test
}

// if embedded type is pointer you must initialized it
type New3Test struct {
    *Test
}

     1.定義型別NewTest 相當於一個新的型別,他不能直接呼叫Test的方法,但是可以訪問Test的欄位。如果想呼叫原型別的方法需要做轉換

     2.New2Test和New3Test都是組合型別,他倆都可以直接呼叫Test的方法和訪問Test的欄位,他倆的不同之處就是一個是值組合一個是指標組合

   3.在例項化New3Test的時候需要手動例項化*Test指標

    n := NewTest{}
    n.N = 1
    // n have no method
    // n.Name() // error
    v := (*Test)(&n)
    v.Name()

    v2 := Test(n)
    v2.Name()

    n2 := New2Test{}
    n2.N = 2
    n2.Name()

    n3 := New3Test{Test: new(Test)}
    // access filed N will panic if you do not initialized *Test
    n3.N = 3
    n3.Name()

  相關的解答:

https://stackoverflow.com/questions/28800672/how-to-add-new-methods-to-an-existing-type-in-go/28800807#28800807

https://golang.org/ref/spec#Type_declarations

 

---

 

相關文章