Go series of tutorials - 29. Defer

Welcome to  Golang tutorial series of 29.

What is defer?

defer Purpose statement is: contains  defer function statement, it will be before the function returns, call another function. This definition may seem very complex, we use an example it is easy to understand.

Examples

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) } 

 

The above procedure is very simple, is to find the maximum value for a given slice. largest A receiving function of type int slice as a parameter, and a maximum value of the print slice. largest The first line of the function statement  defer finished(). This means that  finished() before the function will return, calls the  finished() function. Run the program, you will see the following output:

Started finding largest  
Largest number in [78 109 2 563 300] is 563 Finished finding largest 

largest After the function started, it will print two lines of output above. And in  largest time to be returned, and we call delay function (Deferred Function), print out the  Finished finding largest text.:smile:

Delay method

defer Is not limited to a function call, call the method is legal. We wrote a small program to test it.

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 ") } 

 

In the example above, we delayed a method call on line 22. The rest of the code is straightforward, it is not explained here. The program prints:

Welcome John Smith

The value argument (Arguments Evaluation)

In the Go language, not when calling a delay function arguments before deciding, but when the execution  defer time of the statement, it will delay function arguments are evaluated.

Through an example will be able to understand.

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) } 

 

Line 11, in the above procedures a an initial value of 5. Line 12 performs the  defer time statement, since  a equal to 5, and therefore the delay function  printA arguments is also equal to 5. In the first 13 rows We then  a modified to 10 values. The next line will print out  a the value. The program prints:

value of a before deferred function call 10  
value of a in deferred function 5 

From the above output, we can see that in the call  defer after the statement, although we will  a amend 10, but delayed function call  printA(a), the print is still 5.

defer 栈

When the function is called multiple times within a  defer time, Go will  defer call placed in a stack, and then according to the LIFO (Last In First Out, LIFO) order of execution.

Here we write a small program that uses the  defer stack to a string in reverse print.

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) } } 

 

Line 11 in the above procedure, the for range loop iterates a string, and the call in line 12  defer fmt.Printf("%c", v). These delays are added to a call stack, performed in LIFO order, therefore, the string is printed in reverse. The program will output:

Orignal String: Naveen  
Reversed String: neevaN

Defer the practical application of

So far, the code sample we have seen, did not reflect the  defer actual use. In this section we will look at  defer practical application.

When a function to be independent of the call at the current code stream (Code Flow) environment, it may be used  defer. We used the a  WaitGroup to understand the meaning of this sentence code examples. We will first write an unused  defer program, then we will use  defer to modify, to see  defer the benefits.

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") } 

 

In the above procedure, we created the line 8 in  rect the structure, and a line 13 to create  rect a method  areato calculate the area of the rectangle. area Check that the length and width of the rectangle is less than zero. If the length and width of the rectangle is less than zero, it will print out a corresponding message, and if greater than zero, it will print out a rectangular area.

main Function creates three  rect types of r1variables: , r2 and  r3. On line 34, we add these three variables to  rects slice inside. The sections were then used to  for range cycle through, to  area a method as a concurrent Go coroutine call (line 37). We used  WaitGroup wg to ensure that the  main function after the other coroutine executed, execution will end. WaitGroup Passed as a parameter to  area the method, on line 16, line 21, line 26 and notification  main function, and now it represents a coroutine tasks are complete. If you look closely, you will find  wg.Done() only  area will be called when the function returns. wg.Done() It should be in  area before the call to be returned, and regardless of the path (Path) the code stream, so we can call only once  defer, to effectively replace  wg.Done() multiple calls .

We used to  defer rewrite the above code.

In the following code, we removed the original program 3  wg.Done calls, but with a separate  defer wg.Done() replace it (line 14). This makes our code more concise and easy to understand.

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") } 

 

The program will output:

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 

In the above procedure, using  defer another advantage. Suppose we use  if conditional statements, and gave  area way to add a return path (Return Path). If you do not use  defer to call  wg.Done(), we have a very careful to ensure that in the return path of this newly added in the call  wg.Done(). Now that we call the delay  wg.Done(), there is no need to add to this new return path  wg.Done() of the.

This tutorial is over. wish you happiness.

Guess you like

Origin www.cnblogs.com/fengchuiyizh/p/11356112.html