Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Analysis of the implementation principle of Go defer

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >

Share

Shulou(Shulou.com)05/31 Report--

This article introduces the relevant knowledge of "Analysis of the implementation principle of Go defer". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!

1. Preface

The defer statement is used to delay the call of a function. Each time defer pushes a function into the stack, and the delayed function is taken out and executed before the function returns.

For ease of description, the function that created the defer is called the main function, and the function after the defer statement is called the delay function.

The delay function may have input parameters, which may come from the function that defines defer, or the delay function may refer to the variables returned by the main function, that is, the delay function may affect some of the behavior of the main function. In these scenarios, it is easy to make errors if you do not understand the rules of defer.

In fact, the official statement of the three principles of defer is very clear, this section attempts to summarize the usage scenarios of defer and give a brief explanation.

two。 Warm up

By convention, let's look at a few interesting questions to test our understanding of defer.

2.1 question 1

What is the output of the following function?

Func deferFuncParameter () {var aInt = 1 defer fmt.Println (aInt) aInt = 2 return}

Title description:

The function deferFuncParameter () defines an integer variable and initializes it to 1, then uses the deferent statement to print out the variable value, and finally modifies the variable value to 2.

Reference answer:

Output 1. The parameters of the delay function fmt.Println (aInt) are determined when the defer statement appears, so no matter how you modify the aInt variable later, the delay function will not be affected.

2.2 topic 2

What does the following program output?

Package mainimport "fmt" func printArray (array * [3] int) {for I: = range array {fmt.Println (Array [I])} func deferFuncParameter () {var aArray = [3] int {1,2,3} defer printArray (& aArray) aArray [0] = 10 return} func main () {deferFuncParameter ()}

Function description:

The function deferFuncParameter () defines an array, delays the call to the defer function printArray (), and finally modifies the first element of the array. The printArray () function takes the pointer to the array and prints it all out.

Reference answer:

Output three values of 10, 2, and 3. The parameter of the delay function printArray () is determined when the defer statement appears, that is, the address of the array. Because the delay function is executed before the return statement, the final modification to the array will be printed.

2.3 topic 3

What does the following function output?

Func deferFuncReturn () (result int) {I: = 1 defer func () {result++} () return I}

Function description:

The function has a named return value result, and the function declares a variable iPermine defer that specifies a delay function, and finally returns the variable I. Increment result in the delay function.

Reference answer:

Function output 2. The return statement of the function is not atomic, and the actual execution is divided into setting the return value-- > the ret,defer statement is actually executed before the return, that is, the return process of the function with defer is: set the return value-- > execute defer-- > ret. So the return statement first sets the result to the value of I, that is, the result is incremented by 1 in the result statement, so it finally returns 2.

3. Defer rules

The official Golang blog summarizes defer's rules of conduct, and there are only three, which we will explain around them.

Rule 1: the parameters of the delay function are determined when the defer statement appears

The official gives an example, as follows:

Func a () {I: = 0 defer fmt.Println (I) iTunes + return}

The fmt.Println () parameter I value in the defer statement is determined when defer appears, and is actually a copy. Subsequent changes to the variable I will not affect the execution of the fmt.Println () function, and will still print "0".

Note: for pointer type parameters, the rules still apply, except that the parameter of the delay function is an address value, in which case modifications to variables by statements after defer may affect the delay function.

Rule 2: delay function execution is executed in last-in-first-out order, that is, the last execution of defer that appears first

This rule is easy to understand. Defining a defer is similar to a stack operation, and performing a defer is similar to an unstack operation.

The original purpose of designing defer is to simplify the action of cleaning up resources when the function returns. Resources often have a dependent order, such as applying for A resources first, then B resources according to A resources, and C resources according to B resources, that is, the application order is: AME-> BMY-C, and the release often has to be carried out in reverse. That's why deffer is designed as FIFO.

Every time you apply for a resource that needs to be released, it is a good habit to define a defer to release the resource immediately.

3.3 Rule 3: the delay function may manipulate the named return value of the main function

The function that defines defer, that is, the main function may have a return value, it doesn't matter whether the return value has a name or not, and the function played by defer, that is, the delay function, may affect the return value.

To understand how the delay function affects the return value of the main function, it is sufficient to understand how the function returns.

3.3.1 function return procedure

It is important to understand the fact that the keyword return is not an atomic operation. In fact, return only proxies the assembly instruction ret, which is about to jump into execution. For example, the statement return I is actually carried out in two steps, that is, the I value is stored in the stack as the return value, and then the jump is performed, and the timing of defer execution is just before the jump, so there is still a chance to manipulate the return value when defer is executed.

Give a practical example to illustrate this process:

Func deferFuncReturn () (result int) {I: = 1 defer func () {result++} () return I}

The return statement of this function can be split into the following two lines:

Result = ireturn

The execution of the delay function is performed just before return, that is, after joining defer:

Result = iresult++return

So the above function actually returns the iBond + value.

There are different ways to return the main function, but the return mechanism, as mentioned in the introduction to the computer, can be well understood as long as the return statement is taken apart. Here are some examples.

3.3.1 the main function has an anonymous return value and returns a literal value

A main function has an anonymous return value that returns literally, such as "1", "2", "Hello", in which case the defer statement cannot manipulate the return value.

A function that returns a literal value, as follows:

Func foo () int {var i int defer func () {ionization +} () return 1}

The above return statement directly writes 1 into the stack as the return value, and the delay function cannot operate on the return value, so it cannot affect the return value.

3.3.2 the main function has an anonymous return value and returns variables

A main function has an anonymous return value that uses local or global variables, in which case the defer statement can refer to the return value without changing the return value.

A function that returns a local variable, as follows:

Func foo () int {var i int defer func () {ionization +} () return I}

The above function returns a local variable, and the defer function also manipulates this local variable. For anonymous return values, it can be assumed that there is still a variable storing the return value, and that the return value variable is "anony". The above return statement can be split into the following procedure:

Anony = ii++return

Because I is an integer, the value is copied to anony, so changing the I value in the defer statement has no effect on the return value of the function.

3.3.3 the main function has a named return value

The return value with a name in the main function declaration statement is initialized to a local variable, which can be used inside the function as if it were a local variable. If the defer statement manipulates the return value, the return result may be changed.

An example that affects the return value of a function:

Func foo () (ret int) {defer func () {ret++} () return 0}

The above function is disassembled as follows:

Ret = 0ret++return

Before the function actually returns, it does a + 1 operation on the return value in defer, so the function finally returns 1.

4. The principle of defer implementation

In this section, we try to understand some of the implementation mechanisms of defer.

4.1 defer data structure

The source code package src/src/runtime/runtime2.go:_defer defines the data structure of defer:

Type _ defer struct {sp uintptr / / function stack pointer pc uintptr / / Program counter fn * funcval / / function address link * _ defer / / pointer to its own structure, used to link multiple defer}

We know that defer must be followed by a function, so the data structure of defer is similar to general functions, including stack addresses, program counters, function addresses, and so on.

Unlike the function, it contains a pointer that can be used to point to another defer, and there is actually a defer pointer in each goroutine data structure, which points to a single-linked list of defer. Every time a defer is declared, the defer is inserted into the single-linked list header, and each time defer is executed, a defer execution is taken from the single-linked list header.

The following figure shows a scenario when a goroutine defines multiple defer:

As you can see from the figure above, the newly declared defer is always added to the header of the linked list.

The execution of defer before the return of the function is taken from the head of the linked list in turn and will not be repeated.

A goroutine may call multiple functions successively. The process of adding defer follows the above process. Add defer when entering the function and take out defer when you leave the function. Therefore, even if multiple functions are called, defer can always be executed as FIFO.

4.2 creation and execution of defer

The source package src/runtime/panic.go defines two methods for creating defer and executing defer, respectively.

Deferproc (): called at the declaration defer, which stores the defer function in the linked list of goroutine

Deferreturn (): called on the return instruction, to be exact, before the ret instruction, which fetches defer from the goroutine linked list and executes it.

It can be simply understood that in the compilation phase, the function deferproc () is inserted at the declaration defer, and the function deferreturn () is inserted before the function return.

5. Summary

The delay function parameters defined by defer are determined when the defer statement comes out.

The order of definition of defer is opposite to the order of actual execution.

Return is not an atomic operation, and the execution process is: save the return value (if any)-- > execute defer (if any)-- > perform ret jump

It is a good habit to use defer to close a resource immediately after applying for it.

This is the end of the content of "Analysis of the implementation principle of Go defer". Thank you for your reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Database

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report