@phper 2018-03-13T18:07:03.000000Z 字数 4016 阅读 2572

# 29.推迟(defer)

Golang

## 什么是延迟（defer）？

Defer语句用于在返回defer语句的函数之前执行函数调用。这个定义可能看起来很复杂，但通过一个例子很容易理解。

## 例如

package mainimport (      "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

largest函数开始执行并打印上面的前两行。在它返回之前, 我们的延迟函数finished执行并打印文本Finished finding largest

## 延迟方法 （defer method）

Defer 并不仅仅限于函数（function）。延迟方法（method）调用也是合法的。我们来编写一个小程序来测试它。

package mainimport (      "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


## 参数评估

defer执行语句时会计算延迟函数的参数，而不是实际函数调用完成时的参数。

package mainimport (      "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  

## 延迟堆栈

package mainimport (      "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


## 延迟的实际使用

package mainimport (      "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")}

main函数创建了3个类型为rect的变量r1r2r3。然后将它们添加到第34行中的rects切片中。然后使用for range此切片进行迭代, 并将 range 方法称为37行中的并发area Goroutine 。WaitGroup wg用于确保主函数被阻止, 直到所有 Goroutines 完成执行为止。此 WaitGroup 作为参数传递给区域方法, 并且区域方法调用wg.Done()在16、21和26行中, 通知主要功能 Goroutine 完成其工作。如果您注意到, 则可以看到这些调用恰好在area方法返回之前, 应调用 wg.Done(), 而不考虑代码流所采用的路径, 因此这些调用可以通过一个defer调用有效替换.

package mainimport (      "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  

• 私有
• 公开
• 删除