<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          神奇的 Go init 函數(shù)

          共 2726字,需瀏覽 6分鐘

           ·

          2021-11-18 02:51

          前言

          哈嘍,兄弟們,我是asong。今天與大家聊一聊Go語(yǔ)言中的神奇函數(shù)init,為什么叫他神奇函數(shù)呢?因?yàn)樵摵瘮?shù)可以在所有程序執(zhí)行開(kāi)始前被調(diào)用,并且每個(gè)包下可以有多個(gè)init函數(shù)。這個(gè)函數(shù)使用起來(lái)比較簡(jiǎn)單,但是你們知道他的執(zhí)行順序是怎樣的嘛?本文我們就一起來(lái)解密。

          init函數(shù)的特性

          先簡(jiǎn)單介紹一下init函數(shù)的基本特性:

          • init函數(shù)先于main函數(shù)自動(dòng)執(zhí)行
          • 每個(gè)包中可以有多個(gè)init函數(shù),每個(gè)包中的源文件中也可以有多個(gè)init函數(shù)
          • init函數(shù)沒(méi)有輸入?yún)?shù)、返回值,也未聲明,所以無(wú)法引用
          • 不同包的init函數(shù)按照包導(dǎo)入的依賴關(guān)系決定執(zhí)行順序
          • 無(wú)論包被導(dǎo)入多少次,init函數(shù)只會(huì)被調(diào)用一次,也就是只執(zhí)行一次

          init函數(shù)的執(zhí)行順序

          我在剛學(xué)習(xí)init函數(shù)時(shí)就對(duì)他的執(zhí)行順序很好奇,在谷歌上搜了幾篇文章,他們都有一樣的圖:

          下圖來(lái)源于網(wǎng)絡(luò):

          截屏2021-06-05 上午9.55.15

          這張圖片很清晰的反應(yīng)了init函數(shù)的加載順序:

          • 包加載優(yōu)先級(jí)排在第一位,先層層遞歸進(jìn)行包加載
          • 每個(gè)包中加載順序?yàn)椋?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">const > var > init,首先進(jìn)行初始化的是常量,然后是變量,最后才是init函數(shù)。針對(duì)包級(jí)別的變量初始化順序,Go官方文檔給出這樣一個(gè)例子:
          var?(
          ?a?=?c?+?b??//?==?9
          ?b?=?f()????//?==?4
          ?c?=?f()????//?==?5
          ?d?=?3??????//?==?5?after?initialization?has?finished
          )

          func?f()?int?{
          ?d++
          ?return?d
          }

          變量的初始化按出現(xiàn)的順序從前往后進(jìn)行,假若某個(gè)變量需要依賴其他變量,則被依賴的變量先初始化。所以這個(gè)例子中,初始化順序是 d -> b -> c -> a

          上圖只是表達(dá)了init函數(shù)大概的加載順序,有些細(xì)節(jié)我們還是不知道的,比如:當(dāng)前包下有多個(gè)init函數(shù),按照什么順序執(zhí)行,當(dāng)前源文件下有多個(gè)init函數(shù),這又按照什么順序執(zhí)行呢?本來(lái)想寫個(gè)例子挨個(gè)驗(yàn)證一下的,后來(lái)一看Go官方文檔中都有說(shuō)明,也就沒(méi)有必要再寫一個(gè)例子啦,直接說(shuō)結(jié)論吧:

          • 如果當(dāng)前包下有多個(gè)init函數(shù),首先按照源文件名的字典序從前往后執(zhí)行。
          • 若一個(gè)文件中出現(xiàn)多個(gè)init函數(shù),則按照出現(xiàn)順序從前往后執(zhí)行。

          前面說(shuō)的有點(diǎn)亂,對(duì)init函數(shù)的加載順序做一個(gè)小結(jié):

          從當(dāng)前包開(kāi)始,如果當(dāng)前包包含多個(gè)依賴包,則先初始化依賴包,層層遞歸初始化各個(gè)包,在每一個(gè)包中,按照源文件的字典序從前往后執(zhí)行,每一個(gè)源文件中,優(yōu)先初始化常量、變量,最后初始化init函數(shù),當(dāng)出現(xiàn)多個(gè)init函數(shù)時(shí),則按照順序從前往后依次執(zhí)行,每一個(gè)包完成加載后,遞歸返回,最后在初始化當(dāng)前包!

          init函數(shù)的使用場(chǎng)景

          還記得我之前的這篇文章嗎:go解鎖設(shè)計(jì)模式之單例模式,借用init函數(shù)的加載機(jī)制我們可以實(shí)現(xiàn)單例模式中的餓漢模式,具體怎么實(shí)現(xiàn)可以參考這篇文章,這里就不在寫一遍了。

          init函數(shù)的使用場(chǎng)景還是挺多的,比如進(jìn)行服務(wù)注冊(cè)、進(jìn)行數(shù)據(jù)庫(kù)或各種中間件的初始化連接等。Go的標(biāo)準(zhǔn)庫(kù)中也有許多地方使用到了init函數(shù),比如我們經(jīng)常使用的pprof工具,他就使用到了init函數(shù),在init函數(shù)里面進(jìn)行路由注冊(cè):

          //go/1.15.7/libexec/src/cmd/trace/pprof.go
          func?init()?{
          ?http.HandleFunc("/io",?serveSVGProfile(pprofByGoroutine(computePprofIO)))
          ?http.HandleFunc("/block",?serveSVGProfile(pprofByGoroutine(computePprofBlock)))
          ?http.HandleFunc("/syscall",?serveSVGProfile(pprofByGoroutine(computePprofSyscall)))
          ?http.HandleFunc("/sched",?serveSVGProfile(pprofByGoroutine(computePprofSched)))

          ?http.HandleFunc("/regionio",?serveSVGProfile(pprofByRegion(computePprofIO)))
          ?http.HandleFunc("/regionblock",?serveSVGProfile(pprofByRegion(computePprofBlock)))
          ?http.HandleFunc("/regionsyscall",?serveSVGProfile(pprofByRegion(computePprofSyscall)))
          ?http.HandleFunc("/regionsched",?serveSVGProfile(pprofByRegion(computePprofSched)))
          }

          這里就不擴(kuò)展太多了,更多標(biāo)準(zhǔn)庫(kù)中的使用方法大家可以自己去探索一下。

          在這最后總結(jié)一下使用init要注意的問(wèn)題吧:

          • 編程時(shí)不要依賴init的順序
          • 一個(gè)源文件下可以有多個(gè)init函數(shù),代碼比較長(zhǎng)時(shí)可以考慮分多個(gè)init函數(shù)
          • 復(fù)雜邏輯不建議使用init函數(shù),會(huì)增加代碼的復(fù)雜性,可讀性也會(huì)下降
          • init函數(shù)中也可以啟動(dòng)goroutine,也就是在初始化的同時(shí)啟動(dòng)新的goroutine,這并不會(huì)影響初始化順序
          • init函數(shù)不應(yīng)該依賴任何在main函數(shù)里創(chuàng)建的變量,因?yàn)?code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(40, 202, 113);">init函數(shù)的執(zhí)行是在main函數(shù)之前的
          • init函數(shù)在代碼中不能被顯示的調(diào)用,不能被引用(賦值給函數(shù)變量),否則會(huì)出現(xiàn)編譯錯(cuò)誤。
          • 導(dǎo)入包不要出現(xiàn)循環(huán)依賴,這樣會(huì)導(dǎo)致程序編譯失敗
          • Go程序僅僅想要用一個(gè)packageinit執(zhí)行,我們可以這樣使用:import _ "test_xxxx",導(dǎo)入包的時(shí)候加上下劃線就ok
          • 包級(jí)別的變量初始化、init函數(shù)執(zhí)行,這兩個(gè)操作都是在同一個(gè)goroutine中調(diào)用的,按順序調(diào)用,一次一個(gè)包

          總結(jié)

          好啦,這篇文章到這里就結(jié)束了,本身init函數(shù)就很好理解,寫這篇文章的目的就是讓大家了解他的執(zhí)行順序,這樣在日常開(kāi)發(fā)中才不會(huì)寫出bug。希望本文對(duì)大家有所幫助,我們下期見(jiàn)!

          素質(zhì)三連(分享、點(diǎn)贊、在看)都是筆者持續(xù)創(chuàng)作更多優(yōu)質(zhì)內(nèi)容的動(dòng)力!我是asong,我們下期見(jiàn)。


          瀏覽 65
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  麻豆网站 | YY6080伦理韩国日本 | 天天干 天天添 | 一区二区三区有码 | 三级www |