在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...")
}
效果如下:
圖(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)
}
效果如下:
圖(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, ")
}
效果如下:
圖(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)
}
效果如下:
圖(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)
}
效果如下:
圖(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")
}
效果如下:
圖(6) 延遲某個應用