Golang基礎(chǔ)--常見(jiàn)坑

這里列舉的Go語(yǔ)言常見(jiàn)坑都是符合Golang語(yǔ)法的,可以正常的編譯嘁捷,但是可能是運(yùn)行結(jié)果錯(cuò)誤造成,或者是有資源泄漏的風(fēng)險(xiǎn)。

可變參數(shù)是空接口類型

當(dāng)參數(shù)的可變參數(shù)是空接口類型時(shí)雄嚣,傳人空接口的切片時(shí)需要注意參數(shù)展開(kāi)的問(wèn)題晒屎。

func main() {
    var a = []interface{}{1, 2, 3}
    fmt.Println(a)
    fmt.Println(a...)
}
// 不管是否展開(kāi),編譯器都無(wú)法發(fā)現(xiàn)錯(cuò)誤缓升,但是輸出是不同的:
// [1 2 3]
// 1 2 3

數(shù)組是值傳遞

在函數(shù)調(diào)用參數(shù)中鼓鲁,數(shù)組是值傳遞,無(wú)法通過(guò)修改數(shù)組類型的參數(shù)返回結(jié)果港谊,必要時(shí)需要使用切片骇吭。

func main() {
   x := [3]int{1, 2, 3}
   func(arr [3]int) {
      arr[0] = 7
      fmt.Println(arr)
   }(x)
   fmt.Println(x)
}
// [7 2 3]
// [1 2 3]

map遍歷順序是不固定的

map是一種hash表實(shí)現(xiàn),每次遍歷的順序都可能不一樣歧寺。Golang會(huì)提前取一個(gè)隨機(jī)數(shù)燥狰,把桶的遍歷順序隨機(jī)化。

func main() {
   m := map[string]string{
      "1": "1",
      "2": "2",
      "3": "3",
   }
   for k, v := range m {
      println(k, v)
   }
}

recover必須在defer函數(shù)中運(yùn)行

recover捕獲的是祖父級(jí)調(diào)用時(shí)的異常斜筐,直接調(diào)用無(wú)效龙致;

直接defer調(diào)用無(wú)效

func main() {
   defer recover()
   panic(1)
}

defer調(diào)用時(shí)多層嵌套依然無(wú)效

func main() {
   defer func() {
      func() { recover() }()
   }()
   panic(1)
}

必須在defer函數(shù)中直接調(diào)用才有效

func main() {
   defer func() {
      recover()
   }()
   panic(1)
}

main函數(shù)提前退出

導(dǎo)致后臺(tái)Goroutine無(wú)法保證完成任務(wù)。

休眠:

time.Sleep(time.Second)

插入調(diào)度語(yǔ)句:

runtime.Gosched() //用于讓出當(dāng)前CPU時(shí)間片

利用for:

for {}

利用for+調(diào)度語(yǔ)句:

for {
    runtime.Gosched() 
}

利用阻塞:

select {}

閉包錯(cuò)誤引用同一個(gè)變量

func main() {
   for i := 0; i < 5; i++ {
      defer func() {
         println(i)
      }()
   }
}
/*
5
5
5
5
5
*/

改進(jìn)的方法是在每輪迭代中生成一個(gè)局部變量

func main() {
   for i := 0; i < 5; i++ {
      i := i
      defer func() {
         println(i)
      }()
   }
}
/*
4
3
2
1
0
*/

或者是通過(guò)函數(shù)參數(shù)傳入:

func main() {
   for i := 0; i < 5; i++ {
      defer func(i int) {
         println(i)
      }(i)
   }
}
/*
4
3
2
1
0
*/

在循環(huán)內(nèi)部執(zhí)行defer語(yǔ)句

defer在函數(shù)退出時(shí)才能執(zhí)行顷链,在for執(zhí)行defer會(huì)導(dǎo)致資源延遲釋放:

func main() {
   for i := 0; i < 5; i++ {
      f, err := os.Open("/path/to/file")
      if err != nil {
         log.Fatal(err)
      }
      defer f.Close()
   }
}

解決的方法可以在for中構(gòu)造一個(gè)局部函數(shù)目代,在局部函數(shù)內(nèi)部執(zhí)行defer:

func main() {
   for i := 0; i < 5; i++ {
      func() {
         f, err := os.Open("/path/to/file")
         if err != nil {
            log.Fatal(err)
         }
         defer f.Close()
      }()
   }
}

切片會(huì)導(dǎo)致整個(gè)底層數(shù)組被鎖定

切片會(huì)導(dǎo)致整個(gè)底層數(shù)組被鎖定,底層數(shù)組無(wú)法釋放內(nèi)存。如果底層數(shù)組較大會(huì)對(duì)內(nèi)存產(chǎn)生很大的壓力榛了。

headerMap[name] = data[:1]

解決的方法是將結(jié)果克隆一份在讶,這樣可以釋放底層的數(shù)組:

eaderMap[name] = append([]byte{}, data[:1]...)

空指針和空接口不等價(jià)

返回了一個(gè)錯(cuò)誤指針,但是并不是空的error接口:

內(nèi)存地址會(huì)變化

Go語(yǔ)言中對(duì)象的地址可能發(fā)生變化忽冻,因此指針不能從其它非指針類型的值生成:

func main() {
   var x int = 42
   var p uintptr = uintptr(unsafe.Pointer(&x))
   runtime.GC()
   var px *int = (*int)(unsafe.Pointer(p))
   println(*px)
}

當(dāng)內(nèi)存發(fā)送變化的時(shí)候真朗,相關(guān)的指針會(huì)同步更新,但是非指針類型的uintptr不會(huì)做同步更新僧诚。

同理CGO中也不能保存Go對(duì)象地址。

Goroutine泄露

Go語(yǔ)言是帶內(nèi)存自動(dòng)回收的特性蝗碎,因此內(nèi)存一般不會(huì)泄漏湖笨。但是Goroutine確存在泄漏的情況,同時(shí)泄漏的Goroutine引用的內(nèi)存同樣無(wú)法被回收蹦骑。

func main() {
   ch := func() <-chan int {
      ch := make(chan int)
      go func() {
         for i := 0; ; i++ {
            ch <- i
         }
      } ()
      return ch
   }()
   for v := range ch {
      fmt.Println(v)
      if v == 5 {
         break
      }
   }
}

上面的程序中后臺(tái)Goroutine向管道輸入自然數(shù)序列慈省,main函數(shù)中輸出序列。但是當(dāng)break跳出for循環(huán)的時(shí)候眠菇,后臺(tái)Goroutine就處于無(wú)法被回收的狀態(tài)了边败。

我們可以通過(guò)context包來(lái)避免這個(gè)問(wèn)題:

func main() {
   ctx, cancel := context.WithCancel(context.Background())
   ch := func(ctx context.Context) <-chan int {
      ch := make(chan int)
      go func() {
         for i := 0; ; i++ {
            select {
            case <- ctx.Done():
               return
            case ch <- i:
            }
         }
      } ()
      return ch
   }(ctx)
   for v := range ch {
      fmt.Println(v)
      if v == 5 {
         cancel()
         break
      }
   }
}

當(dāng)main函數(shù)在break跳出循環(huán)時(shí),通過(guò)調(diào)用cancel()來(lái)通知后臺(tái)Goroutine退出捎废,這樣就避免了Goroutine的泄漏笑窜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市登疗,隨后出現(xiàn)的幾起案子排截,更是在濱河造成了極大的恐慌,老刑警劉巖辐益,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件断傲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡智政,警方通過(guò)查閱死者的電腦和手機(jī)认罩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)续捂,“玉大人垦垂,你說(shuō)我怎么就攤上這事〖踩蹋” “怎么了乔外?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)一罩。 經(jīng)常有香客問(wèn)我杨幼,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任差购,我火速辦了婚禮四瘫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘欲逃。我一直安慰自己找蜜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布稳析。 她就那樣靜靜地躺著洗做,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彰居。 梳的紋絲不亂的頭發(fā)上诚纸,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音陈惰,去河邊找鬼畦徘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛抬闯,可吹牛的內(nèi)容都是我干的井辆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼溶握,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼杯缺!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起奈虾,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤夺谁,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后肉微,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體匾鸥,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年碉纳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了勿负。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡劳曹,死狀恐怖奴愉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情铁孵,我是刑警寧澤锭硼,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站蜕劝,受9級(jí)特大地震影響檀头,放射性物質(zhì)發(fā)生泄漏轰异。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一暑始、第九天 我趴在偏房一處隱蔽的房頂上張望搭独。 院中可真熱鬧,春花似錦廊镜、人聲如沸牙肝。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)配椭。三九已至,卻和暖如春雹姊,著一層夾襖步出監(jiān)牢的瞬間颂郎,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工容为, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寺酪。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓坎背,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親寄雀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夕冲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Golang基礎(chǔ)語(yǔ)法 [TOC] 一個(gè)大的程序是由很多小的基礎(chǔ)構(gòu)件組成的际歼。變量保存值,簡(jiǎn)單的加法和減法運(yùn)算被組合成...
    奶爸?jǐn)]代碼閱讀 748評(píng)論 0 4
  • Go語(yǔ)言做Web編程非常方便,并且在開(kāi)發(fā)效率和程序運(yùn)行效率方面都非常優(yōu)秀锅纺。相比于Java,其最大的優(yōu)勢(shì)就是簡(jiǎn)便易用...
    暗黑破壞球嘿哈閱讀 9,006評(píng)論 6 66
  • build-web-application-with-golang 大神astaxie制作的學(xué)習(xí)資料build-w...
    杰_d7f9閱讀 926評(píng)論 0 0
  • 久違的晴天峦阁,家長(zhǎng)會(huì)辆飘。 家長(zhǎng)大會(huì)開(kāi)好到教室時(shí),離放學(xué)已經(jīng)沒(méi)多少時(shí)間了卓嫂。班主任說(shuō)已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)慷暂。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,523評(píng)論 16 22
  • 創(chuàng)業(yè)是很多人的夢(mèng)想,多少人為了理想和不甘選擇了創(chuàng)業(yè)來(lái)實(shí)現(xiàn)自我價(jià)值晨雳,我就是其中一個(gè)行瑞。 創(chuàng)業(yè)后,我由女人變成了超人餐禁,什...
    亦寶寶閱讀 1,810評(píng)論 4 1