原文: https://studygolang.com/articles/12310/comment/17923
--------------------------------------------------------------------------------------
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), 所以通道第一次接收的值就已經改變了, 因為我們傳遞的是引用,不是值類型。
解決方案呢就是加鎖 或者新變量拷貝。