這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
defer
Go語言中有種不錯的設計,即延遲(defer)語句,你可以在函數中添加多個defer語句。當函數執行到最後時,這些defer語句會按照逆序執行,最後該函數返回。特別是當你在進行一些開啟資源的操作時,遇到錯誤需要提前返回,在返回前你需要關閉相應的資源,不然很容易造成資源流失等問題。如下代碼所示,我們一般寫開啟一個資源是這樣操作的:
func ReadWrite() bool { file.Open("file")// 做一些工作 if failureX { file.Close() return false } if failureY { file.Close() return false } file.Close() return true}
我們看到上面有很多重複的代碼,Go的defer
有效解決了這個問題。使用它後,不但代碼量減少了很多,而且程式變得更優雅。在defer
後指定的函數會在函數退出前調用。
func ReadWrite() bool { file.Open("file") defer file.Close() if failureX { return false } if failureY { return false } return true}
如果有很多調用defer
,那麼defer
是採用後進先出模式,所以如下代碼會輸出4 3 2 1 0
for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i)}
defer 給我的第一印象就是,類似java中的
try {
}finally {
}
我目前的理解就是,在函數塊中使用defer,就是函數對應的有一個棧空間,先進後出。需要函數結束後調用棧,來出發defer操作。
如果,一個對象的建立,很消耗記憶體,需要及時關閉,defer無法像try finnaly哪樣準確。
[plain] view plaincopyprint?
- package main
-
- import "fmt"
- import "time"
-
- type User struct {
- username string
- }
-
- func (this *User) Close() {
- fmt.Println(this.username, "Closed !!!")
- }
-
- func main() {
- u1 := &User{"jack"}
- defer u1.Close()
- u2 := &User{"lily"}
- defer u2.Close()
-
- time.Sleep(10 * time.Second)
-
- fmt.Println("Done !")
-
- }
- [vicky@localhost goroutine]$
package mainimport "fmt"import "time"type User struct { username string}func (this *User) Close() { fmt.Println(this.username, "Closed !!!")}func main() { u1 := &User{"jack"} defer u1.Close() u2 := &User{"lily"} defer u2.Close() time.Sleep(10 * time.Second) fmt.Println("Done !")}[vicky@localhost goroutine]$
[vicky@localhost goroutine]$ go run deferTest1.go
Done !
lily Closed !!!
jack Closed !!!
[vicky@localhost goroutine]$
實際上,線程Sleep的10秒,u1,和u2早就可以Close()了,但卻需要依賴main()函數的結束,才能defer執行。
那麼嘗試給defer添加內部代碼區:
[plain] view plaincopyprint?
- package main
-
- import "fmt"
- import "time"
-
- type User struct {
- username string
- }
-
- func (this *User) Close() {
- fmt.Println(this.username, "Closed !!!")
- }
-
- func main() {
- {
- // 即便加了代碼快範圍,依舊也要主函數體結束才執行defer
- u1 := &User{"jack"}
- defer u1.Close()
- }
- u2 := &User{"lily"}
- defer u2.Close()
-
- time.Sleep(10 * time.Second)
-
- fmt.Println("Done !")
-
- }
package mainimport "fmt"import "time"type User struct { username string}func (this *User) Close() { fmt.Println(this.username, "Closed !!!")}func main() { { // 即便加了代碼快範圍,依舊也要主函數體結束才執行defer u1 := &User{"jack"} defer u1.Close() } u2 := &User{"lily"} defer u2.Close() time.Sleep(10 * time.Second) fmt.Println("Done !")}
Done !
lily Closed !!!
jack Closed !!!
[vicky@localhost goroutine]$
依舊defer的執行在Done!後。那麼如何才能達到try finally 哪樣準確的Close呢?
[plain] view plaincopyprint?
- package main
-
- import "fmt"
- import "time"
-
- type User struct {
- username string
- }
-
- func (this *User) Close() {
- fmt.Println(this.username, "Closed !!!")
- }
-
- func main() {
- u1 := &User{"jack"}
- f(u1) // 這樣的方式,u1才會不依賴main函數的執行
-
- // 這樣的方式,u2也不會依賴main函數的執行
- u2 := &User{"lily"}
- // m := func() {
- // defer u2.Close()
- // // u2 do somthing
- // }
- // m()<PRE class=plain name="code"> func() {
- defer u2.Close()
- // u2 do somthing
- }()</PRE> time.Sleep(10 * time.Second) fmt.Println("Done !")}func f(u *User) { defer u.Close() // u1 do gomething}
package mainimport "fmt"import "time"type User struct { username string}func (this *User) Close() { fmt.Println(this.username, "Closed !!!")}func main() { u1 := &User{"jack"} f(u1) // 這樣的方式,u1才會不依賴main函數的執行 // 這樣的方式,u2也不會依賴main函數的執行 u2 := &User{"lily"} // m := func() { // defer u2.Close() // // u2 do somthing // } // m()<div class="dp-highlighter bg_plain"><div class="bar"><div class="tools"><strong>[plain]</strong> <a target=_blank class="ViewSource" title="view plain" href="http://blog.csdn.net/eclipser1987/article/details/12089271#">view plain</a><a target=_blank class="CopyToClipboard" title="copy" href="http://blog.csdn.net/eclipser1987/article/details/12089271#">copy</a><a target=_blank class="PrintSource" title="print" href="http://blog.csdn.net/eclipser1987/article/details/12089271#">print</a><a target=_blank class="About" title="?" href="http://blog.csdn.net/eclipser1987/article/details/12089271#">?</a><a target=_blank href="http://blog.csdn.net/eclipser1987/article/details/12089271"></a></div></div><ol><li class="alt"><span><span>func() { </span></span></li><li><span> defer u2.Close() </span></li><li class="alt"><span> // u2 do somthing </span></li><li><span>}() </span></li></ol></div><pre style="DISPLAY: none" class="plain" name="code"> func() { defer u2.Close() // u2 do somthing }()
time.Sleep(10 * time.Second) fmt.Println("Done !")}func f(u *User) { defer u.Close() // u1 do gomething}
[vicky@localhost goroutine]$ go run deferTest3.go
jack Closed !!!
lily Closed !!!
Done !
這樣的使用方式,視乎不太合理,但卻有存在的必要性。大多數情況下,可以用於 u1,u2 之類非常消耗記憶體,或者cpu,其後執行時間過程且沒有太多關聯的情況。既保留了defer的功能特性,也滿足範圍精確控制的條件!