亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

解鎖即可觀看《Go開發工程師》完整課程視頻

Go開發工程師

未來3-5年企業高性能項目不可替代的語言,從基礎到項目實戰再到重構,真正從入門到精通

【第1周】Go基礎知識入門
【第2周】容器,go編程思想
【第3周】Go并發編程和工程管理
【第4周】從0開始理解rpc和grpc
【第5周】grpc和protobuf進階
【第6周】 yapi文檔管理、gorm詳解
【第7周】gin快速入門
【第8周】用戶服務的grpc服務
【第9周】用戶服務的web服務
【第10周】服務注冊/發現、配置中心、負載均衡
【第11周】商品微服務的grpc服務
【第12周】 商品微服務的gin層和oss圖片服務
【第13周】庫存服務和分布式鎖
【第14周】訂單和購物車微服務
【第15周】 支付寶支付、用戶操作微服務、前后端聯調
【第16周】elasticsearch實現搜索微服務
【第17周】 分布式理論基礎、分布式事務解決方案
【第18周】 學習rocketmq實現冪等性機制等
【第19周】鏈路追蹤、限流、熔斷、降級
【第20周】api網關、部署
【第21周】開發規范和go基礎擴展
【第22周】設計模式和單元測試
【第23周】protoc插件開發、cobra命令行
【第24周】log日志包設計
【第25周】ast代碼生成工具開發
【第26周】三層代碼結構
【第27周】grpc服務封裝更方便的rpc服務
【第28周】深入grpc的服務注冊、負載均衡原理
【第29周】基于gin封裝api服務
【第30周】可觀測的終極解決方案
【第31周】系統監控核心
【第32周】用戶、商品服務重構
【第33周】訂單、庫存等服務重構
【第33+周】訂單服務重構、wire進行ioc控制
【第34周】通過k8s部署服務
【第34+周】devops和k8s
章節
問答
課簽
筆記
評論
占位
占位

Go語言-defer語句

    與select語句一樣,Go語言中的defer語句也非常獨特,而且比前者有過之而無不及。defer語句僅能被放置在函數或方法中。它由關鍵字defer和一個調用表達式組成。注意,這里的調用表達式所表示的既不能是對Go語言內建函數的調用也不能是對Go語言標準庫代碼包unsafe中的那些函數的調用。實際上,滿足上述條件的調用表達式被稱為表達式語句。請看下面的示例:

func readFile(path string) ([]byte, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    return ioutil.ReadAll(file)
}

    函數readFile的功能是讀出指定文件或目錄(以下統稱為文件)本身的內容并將其返回,同時當有錯誤發生時立即向調用方報告。其中,osioutil(導入路徑是io/ioutil)代表的都是Go語言標準庫中的代碼包。請注意這個函數中的倒數第二條語句。我們在打開指定文件且未發現有錯誤發生之后,緊跟了一條defer語句。其中攜帶的表達式語句表示的是對被打開文件的關閉操作。注意,當這條defer語句被執行的時候,其中的這條表達式語句并不會被立即執行。它的確切的執行時機是在其所屬的函數(這里是readFile)的執行即將結束的那個時刻。也就是說,在readFile函數真正結束執行的前一刻,file.Close()才會被執行。這也是defer語句被如此命名的原因。我們在結合上下文之后就可以看出,語句defer file.Close()的含義是在打開文件并讀取其內容后及時地關閉它。該語句可以保證在readFile函數將結果返回給調用方之前,那個文件或目錄一定會被關閉。這實際上是一種非常便捷和有效的保險措施。
   
    更為關鍵的是,無論readFile函數正常地返回了結果還是由于在其執行期間有運行時恐慌發生而被剝奪了流程控制權,其中的file.Close()都會在該函數即將退出那一刻被執行。這就更進一步地保證了資源的及時釋放。
   
    注意,當一個函數中存在多個defer語句時,它們攜帶的表達式語句的執行順序一定是它們的出現順序的倒序。下面的示例可以很好的證明這一點:

func deferIt() {
    defer func() {
        fmt.Print(1)
    }()
    defer func() {
        fmt.Print(2)
    }()
    defer func() {
        fmt.Print(3)
    }()
    fmt.Print(4)
}

    deferIt函數的執行會使標準輸出上打印出4321。請大家猜測下面這個函數被執行時向標準輸出打印的內容,并真正執行它以驗證自己的猜測。最后論證一下自己的猜測為什么是對或者錯的。

func deferIt2() {
    for i := 1; i < 5; i++ {
        defer fmt.Print(i)
    }
}

    最后,對于defer語句,我還有兩個特別提示:
   
    1. defer攜帶的表達式語句代表的是對某個函數或方法的調用。這個調用可能會有參數傳入,比如:fmt.Print(i + 1)。如果代表傳入參數的是一個表達式,那么在defer語句被執行的時候該表達式就會被求值了。注意,這與被攜帶的表達式語句的執行時機是不同的。請揣測下面這段代碼的執行:

func deferIt3() {
    f := func(i int) int {
        fmt.Printf("%d ",i)
        return i * 10
    }
    for i := 1; i < 5; i++ {
        defer fmt.Printf("%d ", f(i))
    }
}

    它在被執行之后,標準輸出上打印出1 2 3 4 40 30 20 10 。
   
    2. 如果defer攜帶的表達式語句代表的是對匿名函數的調用,那么我們就一定要非常警惕。請看下面的示例:

func deferIt4() {
    for i := 1; i < 5; i++ {
        defer func() {
            fmt.Print(i)
        }()
    }
}     

    deferIt4函數在被執行之后標出輸出上會出現5555,而不是4321。原因是defer語句攜帶的表達式語句中的那個匿名函數包含了對外部(確切地說,是該defer語句之外)的變量的使用。注意,等到這個匿名函數要被執行(且會被執行4次)的時候,包含該defer語句的那條for語句已經執行完畢了。此時的變量i的值已經變為了5。因此該匿名函數中的打印函數只會打印出5。正確的用法是:把要使用的外部變量作為參數傳入到匿名函數中。修正后的deferIt4函數如下:

func deferIt4() {
    for i := 1; i < 5; i++ {
        defer func(n int) {
            fmt.Print(n)
        }(i)
    }
}

    請大家自行驗證一下它的正確性。

任務

    命令源碼文件index.go中代碼的功能是打印出斐波那契數列的前10個數,即:0 1 1 2 3 5 8 13 21 34 。請把第 行的fmt.Printf函數調用語句修改成一條defer語句,使得該文件被執行之后標準輸出上會出現:

0 1 1 2 3 5 8 13 21 34 34 21 13 8 5 3 2 1 1 0  

    顯然,這是關于斐波那契數列的一段回文。提示一下,這需要利用前面講到的defer語句的幾個特殊行為。尤其是最后的“特別提示”中講到的一些內容。

?不會了怎么辦

第9行的語句應該被修改成:

defer func(n int) {
    fmt.Printf("%d ", n)
}(func() int {
    n := fibonacci(i)
    fmt.Printf("%d ", n)
    return n
}())
||

提問題

寫筆記

公開筆記
提交
||

請驗證,完成請求

由于請求次數過多,請先驗證,完成再次請求

加群二維碼

打開微信掃碼自動綁定

您還未綁定服務號

綁定后可得到

  • · 粉絲專屬優惠福利
  • · 大咖直播交流干貨
  • · 課程更新,問題答復提醒
  • · 賬號支付安全提醒

收藏課程后,能更快找到我哦~

使用 Ctrl+D 可將課程添加到書簽

邀請您關注公眾號
關注后,及時獲悉本課程動態

舉報

0/150
提交
取消
全部 精華 我要發布
全部 我要發布
最熱 最新
只看我的

手記推薦

更多

本次提問將花費2個積分

你的積分不足,無法發表

為什么扣積分?

本次提問將花費2個積分

繼續發表請點擊 "確定"

為什么扣積分?