二十八.golang的defer
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
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 ") }
在上面的例子中,我们在第 22 行延迟了一个方法调用。而其他的代码很直观,这里不再解释。该程序输出:
Welcome John Smith
在 Go 语言中,并非在调用延迟函数的时候才确定实参,而是当执行 defer 语句的时候,就会对延迟函数的实参进行求值。
通过一个例子就能够理解了。
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
当一个函数内多次调用 defer
我们下面编写一个小程序,使用 defer 栈,将一个字符串逆序打印。
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
目前为止,我们看到的代码示例,都没有体现出 defer 的实际用途。本节我们会看看 defer 的实际应用。
当一个函数应该在与当前代码流(Code Flow)无关的环境下调用时,可以使用 defer。我们通过一个用到了 [WaitGroup] 代码示例来理解这句话的含义。我们首先会写一个没有使用 defer 的程序,然后我们会用 defer 来修改,看到 defer
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") }
在上面的程序里,我们在第 8 行创建了 rect 结构体,并在第 13 行创建了 rect 的方法 area,计算出矩形的面积。area 检查了矩形的长宽是否小于零。如果矩形的长宽小于零,它会打印出对应的提示信息,而如果大于零,它会打印出矩形的面积。
main
我们来用 defer 来重写上面的代码。
在下面的代码中,我们移除了原先程序中的 3 个 wg.Done 的调用,而是用一个单独的 defer wg.Done() 来取代它(第 14 行)。这使得我们的代码更加简洁易懂。
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