Proper use of golang in defer (from in-depth analysis go)

3.4 defer keyword

Go defer and go it is the same as the keyword languages. defer for the release of resources, it will be called before the function returns. Generally use the following modes:

f,err := os.Open(filename)
if err != nil {
    panic(err)
}
defer f.Close()

If there are multiple defer expression, the call stack is similar to the order, defer the expression of the first to be called back.

But if understanding is not deep enough to defer, use may step on some of the pits, especially with the band named return parameters when used together. Before implementing explain defer the first look at the use defer easy encounter.

When the pit defer use

Let's look at a few examples. example 1:

func f() (result int) {
    defer func() {
        result++
    }()
    return 0
}

Example 2:

func f() (r int) {
     t := 5
     defer func() {
       t = t + 5
     }()
     return t
}

Example 3:

func f() (r int) {
    defer func(r int) {
          r = r + 5
    }(r)
    return 1
}

Readers Do not run the code, run it again results in their hearts, and then to verify.

The correct answer is not 0 Example 1, Example 2 is not the correct answer 10 If Example 3 is not the correct answer ...... 6

defer the implementation before the return. In this  official document it is clearly stated in the. Do not step on when you want to use pit defer, the most important thing is to understand, return xxx atomic instruction which is not a statement!

Function returns the process is this: give the return value of the assignment, then call defer expression, and finally returns to the calling function.

defer expression may be set after the function returns a value, before returning to the calling function, modify the return value, the function returns the final values ​​are inconsistent with your imagination.

In fact using defer, with a simple conversion rules rewrite it, it will not confused. Rewrite rule is to write a return statement is split into two, return xxx will be rewritten as:

返回值 = xxx
调用defer函数
空的return

Look Example 1, it can be rewritten as:

func f() (result int) {
     result = 0  //return语句不是一条原子调用,return xxx其实是赋值+ret指令
     func() { //defer被插入到return之前执行,也就是赋返回值和ret指令之间
         result++
     }()
     return
}

So the return value is 1.

Look Example 2, it can be rewritten as:

func f() (r int) {
     t := 5
     r = t //赋值指令
     func() {        //defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过
         t = t + 5
     }
     return        //空的return指令
}

So the result is 5.

Finally, see Example 3, after it is rewritten into:

func f() (r int) {
     r = 1  //给返回值赋值
     func(r int) {        //这里改的r是传值传进去的r,不会改变要返回的那个r值
          r = r + 5
     }(r)
     return        //空的return
}

So the result of this example is 1.

defer indeed before the return call. But it may not be as form of expression. The reason is the nature of a return xxx statement is not atomic instructions, defer is inserted into between the assignment and ret, so there may be an opportunity to change the final return value.

Defer realization of

Go to achieve defer keywords with keywords is very similar, except that it calls is runtime.deferproc instead runtime.newproc.

Where defer appears, the inserted instructions call runtime.deferproc, then place before the function returns, the insertion call runtime.deferreturn.

When the function returns a normal, similar to assembly code:

add xx SP
return

If it contains the defer statement, the assembler code is:

call runtime.deferreturn,
add xx SP
return

Goroutine control structure, there are a table record defer, defer will need when calling runtime.deferproc expressions recorded in the table, and when the call runtime.deferreturn will turn the stack from the table and defer execution.

Guess you like

Origin www.cnblogs.com/lgh344902118/p/11484781.html