<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                什么是defer?如何理解 defer 關鍵字?Go 中使用 defer 的一些坑。 defer 意為延遲,在 golang 中用于延遲執行一個函數。它可以幫助我們處理容易忽略的問題,如資源釋放、連接關閉等。但在實際使用過程中,有一些需要注意的地方. 1. 若函數中有多個 defer,其執行順序為 先進后出,可以理解為棧。 ~~~go package main import "fmt" func main() { for i := 0; i < 5; i++ { defer fmt.Println(i) } } ~~~ 運行: ~~~go 4 3 2 1 0 ~~~ 2. return 會做什么呢? Go 的函數返回值是通過堆棧返回的, return 語句不是原子操作,而是被拆成了兩步. * 給返回值賦值 (rval) * 調用 defer 表達式 * 返回給調用函數(ret) ~~~go package main import "fmt" func main() { fmt.Println(increase(1)) } func increase(d int) (ret int) { defer func() { ret++ }() return d } ~~~ 運行輸出: ~~~go 2 ~~~ 3. 若 defer 表達式有返回值,將會被丟棄。 閉包與匿名函數. * 匿名函數:沒有函數名的函數。 * 閉包:可以使用另外一個函數作用域中的變量的函數。 在實際開發中,defer 的使用經常伴隨著閉包與匿名函數的使用。 ~~~go package main import "fmt" func main() { for i := 0; i < 5; i++ { defer func() { fmt.Println(i) }() } } ~~~ 運行輸出: ~~~go 5 5 5 5 5 ~~~ 之所以這樣是因為,defer 表達式中的 i 是對 for 循環中 i 的引用。到最后,i 加到 5,故最后全部打印 5。 如果將 i 作為參數傳入 defer 表達式中,在傳入最初就會進行求值保存,只是沒有執行延遲函數而已。 應用示例: ~~~go func f1() (result int) { defer func() { result++ }() return 0 } ~~~ ~~~go func f2() (r int) { t := 5 defer func() { t = t + 5 }() return t } ~~~ ~~~go func f3() (r int) { defer func(r int) { r = r + 5 }(r) return 1 } ~~~ ~~~go type Test struct { Max int } func (t *Test) Println() { fmt.Println(t.Max) } func deferExec(f func()) { f() } func call() { var t *Test defer deferExec(t.Println) t = new(Test) } ~~~ 有沒有得出結果?例1的答案不是 0,例2的答案不是 10,例3的答案也不是 6。 defer是在return之前執行的。這個在[官方文檔](https://golang.org/ref/spec#defer_statements)中是明確說明了的。要使用defer時不踩坑,最重要的一點就是要明白,`return xxx`這一條語句并不是一條原子指令! ~~~gfm 函數返回的過程是這樣的:先給返回值賦值,然后調用defer表達式,最后才是返回到調用函數中。 defer表達式可能會在設置函數返回值之后,在返回到調用函數之前,修改返回值,使最終的函數返回值與你想象的不一致。 其實使用defer時,用一個簡單的轉換規則改寫一下,就不會迷糊了。改寫規則是將return語句拆成兩句寫,return xxx會被改寫成: 返回值 = xxx 調用defer函數 空的return ~~~ f1: 比較簡單,參考結論2,將 0 賦給 result,defer 延遲函數修改 result,最后返回給調用函數。正確答案是 1。 f1可以修改成長這樣的: ~~~go func f() (result int) { result = 0 // return語句不是一條原子調用,return xxx其實是賦值+ret指令 func() { // defer被插入到return之前執行,也就是賦返回值和ret指令之間 result++ }() return } ~~~ 所以這個返回值是1。 f2: defer 是在 t 賦值給 r 之后執行的,而 defer 延遲函數只改變了 t 的值,r 不變。正確答案 5。 f2可以修改成這樣的: ~~~go func f() (r int) { t := 5 r = t // 賦值指令 func() { // defer被插入到賦值與返回之間執行,這個例子中返回值r沒被修改過 t = t + 5 } return // 空的return指令 } ~~~ 所以這個的結果是5。 f3: 這里將 r 作為參數傳入了 defer 表達式。故 func (r int) 中的 r 非 func f() (r int) 中的 r,只是參數命名相同而已。正確答案 1。 f3可以修改成這樣的: ~~~go func f() (r int) { r = 1 // 給返回值賦值 func(r int) { // 這里改的r是傳值傳進去的r,不會改變要返回的那個r值 r = r + 5 }(r) return // 空的return } ~~~ 所以這個例子的結果是1。 f4: 這里將發生 panic。將方法傳給 deferExec,實際上在傳的過程中對方法求了值。而此時的 t 任然為 nil。 因此, defer確實是在return之前調用的。但表現形式上卻可能不像。根本原因是`return xxx`語句并不是一條原子指令,defer被插入到了賦值 與 ret之間,因此可能有機會改變最終的返回值。 defer關鍵字的實現跟go關鍵字很類似,不同的是它調用的是`runtime.deferproc`而不是`runtime.newproc`。 在defer出現的地方,插入了指令`call runtime.deferproc`,然后在函數返回之前的地方,插入指令`call runtime.deferreturn`。 普通的函數返回時,匯編代碼類似: ~~~go add xx SP return ~~~ 如果其中包含了defer語句,則匯編代碼是: ~~~go call runtime.deferreturn, add xx SP return ~~~ goroutine的控制結構中,有一張表記錄defer,調用`runtime.deferproc`時會將需要defer的表達式記錄在表中,而在調用`runtime.deferreturn`的時候,則會依次從defer表中出棧并執行。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看