天天看點

Go語言中的defer延時機制

    在Go語言中,有時候需要對某個方法、函數或者參數進行延時處理,這時就需要defer關鍵字。

defer延時的作用

一、延遲函數

  • 可以在函數中添加多個defer語句。

    1)當函數執⾏到最後時,這些defer語句會按照逆序執⾏,最後該函數傳回。特别是當你在進⾏⼀些打開資源的操作時,遇到錯誤需要提前傳回,在傳回前你需要關閉相應的資源,不然很容易造成資源洩露等問題;

    2)如果有很多調⽤defer,那麼defer是采⽤後進先出模式;

    3)在離開所在的⽅法時,執行defer它自己(報錯的時候也會執⾏)

    案例1. 在主函數中延遲調用某個函數

//myDeferFuc.go 在main()裡延遲支援func A()

// myDeferDes project main.go
package main

import (
	"fmt"
)

func funA() {
	fmt.Println("我是funA()...")
}

func funB() {
	fmt.Println("我是funB()...")
}

func funC() {
	fmt.Println("我是funC()...")
}

func main() {
	defer funA()
	funB()
	funC()
	fmt.Println("main is over...")
}


           

    效果如下:

Go語言中的defer延時機制

圖(1) 在main()中延遲執行func A()

    案例2. 在子函數中延遲調用某個函數

//myDeferFunc2.go 在子函數中延遲執行func finished()

// myDeferMax project main.go
package main

import (
	"fmt"
)

func finished() {
	fmt.Println("結束!")
}

func largest(s []int) {
	defer finished()
	fmt.Println("開始尋找最大數...")
	max := s[0]
	for _, v := range s {
		if v > max {
			max = v
		}
	}
	fmt.Printf("%v中的最大數為:%v\n", s, max)
}

func main() {
	s1 := []int{78, 109, 2, 563, 300}
	largest(s1)
}


           

    效果如下:

Go語言中的defer延時機制

圖(2) 在子函數中延遲調用func finished()

二、延遲方法

  • 可以使用defer來延遲某個方法的調用

    案例3. 在主函數中延遲調用某個方法

//myDeferMethod.go

// myDeferMethod project main.go
package main

import (
	"fmt"
)

type person struct {
	firstName string
	lastName  string
}

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

func main() {
	per := person{"Steven", "Wang"}
	defer per.fullName()
	fmt.Printf("Welcome, ")
}


           

    效果如下:

Go語言中的defer延時機制

圖(3) 在主函數中延遲fullName()方法的調用

三、延遲參數傳遞(保留參數)

  • defer會保留在它聲明之前的參數,并在函數的最後才執行。

    案例4. 保留參數a和b, 使用defer

//myDeferMethod.go

// myDeferParam project main.go
package main

import (
	"fmt"
)

func printAdd(a, b int) {
	fmt.Printf("延遲函數中: 參數a,b分别為%d,%d, 兩數之和為:%d\n", a, b, a+b)
}

func main() {
	a := 5
	b := 6
	defer printAdd(a, b) //延遲參數a,b的傳遞
	a = 10
	b = 7
	fmt.Printf("延遲函數執行前: 參數a,b分别為%d,%d, 兩數之和為:%d\n", a, b, a+b)

}


           

    效果如下:

Go語言中的defer延時機制

圖(4) defer會保留在它聲明之前的參數,并在函數的最後才執行

四、堆棧的推遲

  • 當⼀個函數有多個延遲調⽤時,它們被添加到⼀個堆棧中,并在Last In First Out(LIFO)後進先出的順序中執⾏。

    案例5. 利用defer實作字元串倒序

//myDeferReveser.go

// myDeferHeap project main.go
package main

import (
	"fmt"
)

func ReverseString(str string) {
	for _, v := range []rune(str) {
		defer fmt.Printf("%c", v)
	}
}

func main() {
	name := "StevenWang歡迎學習區塊鍊"
	fmt.Println("原始字元串: ", name)
	fmt.Println("翻轉後的字元串: ")
	ReverseString(name)
}


           

    效果如下:

Go語言中的defer延時機制

圖(5) 翻轉字元串

五、延遲某個應用

  • 推薦使用WaitGroup

    案例6 延遲某個應用

//myDeferApp.go

// myDeferApp project main.go
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")
}


           

    效果如下:

Go語言中的defer延時機制

圖(6) 延遲某個應用