[关闭]
@adamhand 2019-02-14T17:17:00.000000Z 字数 2194 阅读 741

golang--defer


defer关键字的含义是:延迟函数(Deferred Function

defer 语句的用途是:含有 defer 语句的函数,会在该函数将要返回之前,调用另一个函数。

例子

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func finished() {
  6. fmt.Println("function has finished")
  7. }
  8. func findLagest(nums []int) {
  9. defer finished()
  10. max := nums[0]
  11. for _, v := range nums{
  12. if v > max{
  13. max = v
  14. }
  15. }
  16. fmt.Println("the largest number is ", max)
  17. }
  18. func main() {
  19. nums := []int{1,2,3,4,5,6,7,8,9}
  20. findLagest(nums)
  21. }

上述findLagest函数在执行完之后会调用finished函数。上述程序的执行结果为:

  1. the largest number is 9
  2. function has finished

延迟方法

defer 不仅限于函数的调用,调用方法也是合法的。

  1. package main
  2. import "fmt"
  3. type person struct {
  4. firstName string
  5. lastName string
  6. }
  7. func (p person)fullName() {
  8. fmt.Printf("%s", p.firstName + p.lastName)
  9. }
  10. func main() {
  11. p := person{
  12. firstName:"Bob",
  13. lastName:"Steven",
  14. }
  15. defer p.fullName()
  16. fmt.Print("welcome ")
  17. }

上述程序的执行结果为:

  1. welcome BobSteven

实参取值

在 Go 语言中,并非在调用延迟函数的时候才确定实参,而是当执行 defer 语句的时候,就会对延迟函数的实参进行求值

  1. package main
  2. import "fmt"
  3. func printValue(v int) {
  4. fmt.Printf("value of v is %d\n", v)
  5. }
  6. func main() {
  7. v := 5
  8. defer printValue(v)
  9. v = 10
  10. fmt.Printf("value of v is %d\n", v)
  11. }

defer 栈

当一个函数内多次调用 defer 时,Go 会把 defer 调用放入到一个栈中,随后按照后进先出(Last In First Out, LIFO)的顺序执行。

  1. package main
  2. import "fmt"
  3. func main() {
  4. name := "stevem bob"
  5. fmt.Printf("name is: %s\n", name)
  6. fmt.Printf("reverse name is: ")
  7. for _, v := range []rune(name){
  8. defer fmt.Printf("%c", v)
  9. }
  10. }

上述程序的执行结果为:

  1. name is: stevem bob
  2. reverse name is: bob mevets

defer 的实际应用

当一个函数应该在与当前代码流(Code Flow)无关的环境下调用时,可以使用 defer。

下面首先会写一个没有使用 defer 的程序,然后用 defer 来修改,看到 defer 带来的好处。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. type rect struct {
  7. length int
  8. width int
  9. }
  10. func (r rect)area(wg *sync.WaitGroup) {
  11. if r.length < 0{
  12. fmt.Printf("length should greater than zero\n")
  13. wg.Done()
  14. return
  15. }
  16. if r.width < 0{
  17. fmt.Printf("width should greater than zero\n")
  18. wg.Done()
  19. return
  20. }
  21. area := r.length * r.width
  22. fmt.Printf("area is %d\n", area)
  23. wg.Done()
  24. }
  25. func main() {
  26. var wg sync.WaitGroup
  27. r1 := rect{-67, 89}
  28. r2 := rect{5, -67}
  29. r3 := rect{8, 9}
  30. rects := []rect{r1, r2, r3}
  31. for _, v := range rects {
  32. wg.Add(1)
  33. go v.area(&wg)
  34. }
  35. wg.Wait()
  36. fmt.Println("All go routines finished executing")
  37. }

仔细观察上述程序,会发现 wg.Done() 只在 area 函数返回的时候才会调用。wg.Done() 应该在 area 将要返回之前调用,并且与代码流的路径(Path)无关,因此我们可以只调用一次 defer,来有效地替换掉 wg.Done() 的多次调用

下面使用defer来改写程序中的area()函数。

  1. func (r rect)area(wg *sync.WaitGroup) {
  2. defer wg.Done()
  3. if r.length < 0{
  4. fmt.Printf("length should greater than zero\n")
  5. return
  6. }
  7. if r.width < 0{
  8. fmt.Printf("width should greater than zero\n")
  9. return
  10. }
  11. area := r.length * r.width
  12. fmt.Printf("area is %d\n", area)
  13. }

更改后的程序可以和之前的程序达到相同的效果。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注