1. 程式人生 > >Go學習(14):defer

Go學習(14):defer

defer

1.1 延遲是什麼?

即延遲(defer)語句,延遲語句被用於執行一個函式呼叫,在這個函式之前,延遲語句返回。

1.2 延遲函式

你可以在函式中新增多個defer語句。當函式執行到最後時,這些defer語句會按照逆序執行,最後該函式返回。特別是當你在進行一些開啟資源的操作時,遇到錯誤需要提前返回,在返回前你需要關閉相應的資源,不然很容易造成資源洩露等問題

  • 如果有很多呼叫defer,那麼defer是採用後進先出模式
  • 在離開所在的方法時,執行(報錯的時候也會執行)
func ReadWrite() bool {
    file.
Open("file") defer file.Close() if failureX { return false } i f failureY { return false } return true }

最後才執行file.Close()

示例程式碼:

package main

import "fmt"

func main() {
	a := 1
	b := 2
	defer fmt.Println(b)
	fmt.Println(a)
}

執行結果:

1
2

示例程式碼:

package main

import (  
    "fmt"
)

func finished() {  
    fmt.Println("Finished finding largest")
}

func largest(nums []int) {  
    defer finished()    
    fmt.Println("Started finding largest")
    max := nums[0]
    for _, v := range nums {
        if v > max {
            max = v
        }
} fmt.Println("Largest number in", nums, "is", max) } func main() { nums := []int{78, 109, 2, 563, 300} largest(nums) }

執行結果:

Started finding largest  
Largest number in [78 109 2 563 300] is 563  
Finished finding largest 

1.3 延遲方法

延遲並不僅僅侷限於函式。延遲一個方法呼叫也是完全合法的。讓我們編寫一個小程式來測試這個。

示例程式碼:

package main

import (  
    "fmt"
)


type person struct {  
    firstName string
    lastName string
}

func (p person) fullName() {  
    fmt.Printf("%s %s",p.firstName,p.lastName)
}

func main() {  
    p := person {
        firstName: "John",
        lastName: "Smith",
    }
    defer p.fullName()
    fmt.Printf("Welcome ")  
}

執行結果:

Welcome John Smith 

1.4 延遲引數

延遲函式的引數在執行延遲語句時被執行,而不是在執行實際的函式呼叫時執行。

讓我們通過一個例子來理解這個問題。

示例程式碼:

package main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.Println("value of a before deferred function call", a)

}

執行結果:

value of a before deferred function call 10  
value of a in deferred function 5 

1.5 堆疊的推遲

當一個函式有多個延遲呼叫時,它們被新增到一個堆疊中,並在Last In First Out(LIFO)後進先出的順序中執行。

我們將編寫一個小程式,它使用一堆defers列印一個字串。示例程式碼:

package main

import (  
    "fmt"
)

func main() {  
    name := "Naveen"
    fmt.Printf("Orignal String: %s\n", string(name))
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}

執行結果:

Orignal String: Naveen  
Reversed String: neevaN 

1.6 延遲的應用

到目前為止,我們所寫的示例程式碼,並沒有實際的應用。現在我們看一下關於延遲的應用。在不考慮程式碼流的情況下,延遲被執行。讓我們以一個使用WaitGroup的程式示例來理解這個問題。我們將首先編寫程式而不使用延遲,然後我們將修改它以使用延遲,並理解延遲是多麼有用。

示例程式碼:

package main

import (  
    "fmt"
    "sync"
)

type rect struct {  
    length int
    width  int
}

func (r rect) area(wg *sync.WaitGroup) {  
    if r.length < 0 {
        fmt.Printf("rect %v's length should be greater than zero\n", r)
        wg.Done()
        return
    }
    if r.width < 0 {
        fmt.Printf("rect %v's width should be greater than zero\n", r)
        wg.Done()
        return
    }
    area := r.length * r.width
    fmt.Printf("rect %v's area %d\n", r, area)
    wg.Done()
}
func main() {  
    var wg sync.WaitGroup
    r1 := rect{-67, 89}
    r2 := rect{5, -67}
    r3 := rect{8, 9}
    rects := []rect{r1, r2, r3}
    for _, v := range rects {
        wg.Add(1)
        go v.area(&wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}

修改以上程式碼:

package main

import (  
    "fmt"
    "sync"
)

type rect struct {  
    length int
    width  int
}

func (r rect) area(wg *sync.WaitGroup) {  
    defer wg.Done()
    if r.length < 0 {
        fmt.Printf("rect %v's length should be greater than zero\n", r)
        return
    }
    if r.width < 0 {
        fmt.Printf("rect %v's width should be greater than zero\n", r)
        return
    }
    area := r.length * r.width
    fmt.Printf("rect %v's area %d\n", r, area)
}
func main() {  
    var wg sync.WaitGroup
    r1 := rect{-67, 89}
    r2 := rect{5, -67}
    r3 := rect{8, 9}
    rects := []rect{r1, r2, r3}
    for _, v := range rects {
        wg.Add(1)
        go v.area(&wg)
    }
    wg.Wait()
    fmt.Println("All go routines finished executing")
}

程式執行結果:

rect {8 9}'s area 72  
rect {-67 89}'s length should be greater than zero  
rect {5 -67}'s width should be greater than zero  
All go routines finished executing