<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                Golang延遲調用: defer特性: ~~~ 1. 關鍵字 defer 用于注冊延遲調用。 2. 這些調用直到 return 前才被執。因此,可以用來做資源清理。 3. 多個defer語句,按先進后出的方式執行。 4. defer語句中的變量,在defer聲明時就決定了。 ~~~ defer用途: ~~~ 1. 關閉文件句柄 2. 鎖資源釋放 3. 數據庫連接釋放 ~~~ go語言 defer go 語言的defer功能強大,對于資源管理非常方便,但是如果沒用好,也會有陷阱。 defer 是先進后出   這個很自然,后面的語句會依賴前面的資源,因此如果先前面的資源先釋放了,后面的語句就沒法執行了。 ~~~ package main import "fmt" func main() { var whatever [5]struct{} for i := range whatever { defer fmt.Println(i) } } ~~~ 輸出結果: ~~~ 4 3 2 1 0 ~~~ defer 碰上閉包 ~~~ package main import "fmt" func main() { var whatever [5]struct{} for i := range whatever { defer func() { fmt.Println(i) }() } } ~~~ 輸出結果: ~~~ 4 4 4 4 4 ~~~ 其實go說的很清楚,我們一起來看看go spec如何說的 Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked. 也就是說函數正常執行,由于閉包用到的變量 i 在執行的時候已經變成4,所以輸出全都是4. defer f.Close 這個大家用的都很頻繁,但是go語言編程舉了一個可能一不小心會犯錯的例子. ~~~ package main import "fmt" type Test struct { name string } func (t *Test) Close() { fmt.Println(t.name, " closed") } func main() { ts := []Test{{"a"}, {"b"}, {"c"}} for _, t := range ts { defer t.Close() } } ~~~ 輸出結果: ~~~ c closed c closed c closed ~~~ 這個輸出并不會像我們預計的輸出c b a,而是輸出c c c 可是按照前面的go spec中的說明,應該輸出c b a才對啊. 那我們換一種方式來調用一下. ~~~ package main import "fmt" type Test struct { name string } func (t *Test) Close() { fmt.Println(t.name, " closed") } func Close(t Test) { t.Close() } func main() { ts := []Test{{"a"}, {"b"}, {"c"}} for _, t := range ts { defer Close(t) } } ~~~ 輸出結果: ~~~ c closed b closed a closed ~~~ 這個時候輸出的就是c b a 當然,如果你不想多寫一個函數,也很簡單,可以像下面這樣,同樣會輸出c b a 看似多此一舉的聲明 ~~~ package main import "fmt" type Test struct { name string } func (t *Test) Close() { fmt.Println(t.name, " closed") } func main() { ts := []Test{{"a"}, {"b"}, {"c"}} for _, t := range ts { t2 := t defer t2.Close() } } ~~~ 輸出結果: ~~~ c closed b closed a closed ~~~ 通過以上例子,結合 Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked. 這句話。可以得出下面的結論: defer后面的語句在執行的時候,函數調用的參數會被保存起來,但是不執行。也就是復制了一份。但是并沒有說struct這里的this指針如何處理,通過這個例子可以看出go語言并沒有把這個明確寫出來的this指針當作參數來看待。 多個 defer 注冊,按 FILO 次序執行 ( 先進后出 )。哪怕函數或某個延遲調用發生錯誤,這些調用依舊會被執行。 ~~~ package main func test(x int) { defer println("a") defer println("b") defer func() { println(100 / x) // div0 異常未被捕獲,逐步往外傳遞,最終終止進程。 }() defer println("c") } func main() { test(0) } ~~~ 輸出結果: ~~~ c b a panic: runtime error: integer divide by zero ~~~ *延遲調用參數在注冊時求值或復制,可用指針或閉包 "延遲" 讀取。 ~~~ package main func test() { x, y := 10, 20 defer func(i int) { println("defer:", i, y) // y 閉包引用 }(x) // x 被復制 x += 10 y += 100 println("x =", x, "y =", y) } func main() { test() } ~~~ 輸出結果: ~~~ x = 20 y = 120 defer: 10 120 ~~~ *濫用 defer 可能會導致性能問題,尤其是在一個 "大循環" 里。 ~~~ package main import ( "fmt" "sync" "time" ) var lock sync.Mutex func test() { lock.Lock() lock.Unlock() } func testdefer() { lock.Lock() defer lock.Unlock() } func main() { func() { t1 := time.Now() for i := 0; i < 10000; i++ { test() } elapsed := time.Since(t1) fmt.Println("test elapsed: ", elapsed) }() func() { t1 := time.Now() for i := 0; i < 10000; i++ { testdefer() } elapsed := time.Since(t1) fmt.Println("testdefer elapsed: ", elapsed) }() } ~~~ 輸出結果: ~~~ test elapsed: 223.162μs testdefer elapsed: 781.304μs ~~~ defer陷阱 defer 與 closure ~~~ package main import ( "errors" "fmt" ) func foo(a, b int) (i int, err error) { defer fmt.Printf("first defer err %v\n", err) defer func(err error) { fmt.Printf("second defer err %v\n", err) }(err) defer func() { fmt.Printf("third defer err %v\n", err) }() if b == 0 { err = errors.New("divided by zero!") return } i = a / b return } func main() { foo(2, 0) } ~~~ 輸出結果: ~~~ third defer err divided by zero! second defer err <nil> first defer err <nil> ~~~ 解釋:如果 defer 后面跟的不是一個 closure 最后執行的時候我們得到的并不是最新的值。 defer 與 return ~~~ package main import "fmt" func foo() (i int) { i = 0 defer func() { fmt.Println(i) }() return 2 } func main() { foo() } ~~~ 輸出結果: ~~~ 2 ~~~ 解釋:在有具名返回值的函數中(這里具名返回值為 i),執行 return 2 的時候實際上已經將 i 的值重新賦值為 2。所以defer closure 輸出結果為 2 而不是 1。 defer nil 函數 ~~~ package main import ( "fmt" ) func test() { var run func() = nil defer run() fmt.Println("runs") } func main() { defer func() { if err := recover(); err != nil { fmt.Println(err) } }() test() } ~~~ 輸出結果: ~~~ runs runtime error: invalid memory address or nil pointer dereference ~~~ 解釋:名為 test 的函數一直運行至結束,然后 defer 函數會被執行且會因為值為 nil 而產生 panic 異常。然而值得注意的是,run() 的聲明是沒有問題,因為在test函數運行完成后它才會被調用。 在錯誤的位置使用 defer 當 http.Get 失敗時會拋出異常。 ~~~ package main import "net/http" func do() error { res, err := http.Get("http://www.google.com") defer res.Body.Close() if err != nil { return err } // ..code... return nil } func main() { do() } ~~~ 輸出結果: ~~~ panic: runtime error: invalid memory address or nil pointer dereference ~~~ 因為在這里我們并沒有檢查我們的請求是否成功執行,當它失敗的時候,我們訪問了 Body 中的空變量 res ,因此會拋出異常 解決方案 總是在一次成功的資源分配下面使用 defer ,對于這種情況來說意味著:當且僅當 http.Get 成功執行時才使用 defer ~~~ package main import "net/http" func do() error { res, err := http.Get("http://xxxxxxxxxx") if res != nil { defer res.Body.Close() } if err != nil { return err } // ..code... return nil } func main() { do() } ~~~ 在上述的代碼中,當有錯誤的時候,err 會被返回,否則當整個函數返回的時候,會關閉 res.Body 。 解釋:在這里,你同樣需要檢查 res 的值是否為 nil ,這是 http.Get 中的一個警告。通常情況下,出錯的時候,返回的內容應為空并且錯誤會被返回,可當你獲得的是一個重定向 error 時, res 的值并不會為 nil ,但其又會將錯誤返回。上面的代碼保證了無論如何 Body 都會被關閉,如果你沒有打算使用其中的數據,那么你還需要丟棄已經接收的數據。 不檢查錯誤 在這里,f.Close() 可能會返回一個錯誤,可這個錯誤會被我們忽略掉 ~~~ package main import "os" func do() error { f, err := os.Open("book.txt") if err != nil { return err } if f != nil { defer f.Close() } // ..code... return nil } func main() { do() } ~~~ 改進一下 ~~~ package main import "os" func do() error { f, err := os.Open("book.txt") if err != nil { return err } if f != nil { defer func() { if err := f.Close(); err != nil { // log etc } }() } // ..code... return nil } func main() { do() } ~~~ 再改進一下 通過命名的返回變量來返回 defer 內的錯誤。 ~~~ package main import "os" func do() (err error) { f, err := os.Open("book.txt") if err != nil { return err } if f != nil { defer func() { if ferr := f.Close(); ferr != nil { err = ferr } }() } // ..code... return nil } func main() { do() } ~~~ 釋放相同的資源 如果你嘗試使用相同的變量釋放不同的資源,那么這個操作可能無法正常執行。 ~~~ package main import ( "fmt" "os" ) func do() error { f, err := os.Open("book.txt") if err != nil { return err } if f != nil { defer func() { if err := f.Close(); err != nil { fmt.Printf("defer close book.txt err %v\n", err) } }() } // ..code... f, err = os.Open("another-book.txt") if err != nil { return err } if f != nil { defer func() { if err := f.Close(); err != nil { fmt.Printf("defer close another-book.txt err %v\n", err) } }() } return nil } func main() { do() } ~~~ 輸出結果: defer close book.txt err close ./another-book.txt: file already closed 當延遲函數執行時,只有最后一個變量會被用到,因此,f 變量 會成為最后那個資源 (another-book.txt)。而且兩個 defer 都會將這個資源作為最后的資源來關閉 解決方案: ~~~ package main import ( "fmt" "io" "os" ) func do() error { f, err := os.Open("book.txt") if err != nil { return err } if f != nil { defer func(f io.Closer) { if err := f.Close(); err != nil { fmt.Printf("defer close book.txt err %v\n", err) } }(f) } // ..code... f, err = os.Open("another-book.txt") if err != nil { return err } if f != nil { defer func(f io.Closer) { if err := f.Close(); err != nil { fmt.Printf("defer close another-book.txt err %v\n", err) } }(f) } return nil } func main() { do() } ~~~
                  <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>

                              哎呀哎呀视频在线观看