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

Example Analysis of defer, panic and recover in Go

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

Share

Shulou(Shulou.com)06/03 Report--

This article will explain in detail the example analysis of defer and panic as well as recover in Go. The content of the article is of high quality, so the editor shares it with you for reference. I hope you will have some understanding of the relevant knowledge after reading this article.

Defer

The defer statement puts a function on a stack, and defer executes the passed function before the current function returns, which is often used to close file descriptors, database connections, redis connections, etc., to clean up resources and avoid waste of resources. Like this chestnut below.

Package mainimport ("fmt"goapp/src/math") func main () {sum:=math.Add (2p3) fmt.Println (sum) defer func () {fmt.Println ("I am defer1")} () res: = test_defer (); fmt.Println (res)} func test_defer () float64 {defer func () {fmt.Println ("I am defer2")} () defer func () {fmt.Println ("I am defer3")} () res: = math3.Mod ("I am defer3") return res;}

What is the result of the implementation?

Perform an addition and print the return value of 5

2.defer1 stack

3. Execute the function test_defer,defer2 into the stack, defer3 into the stack, execute the function logic. Before return, it will execute the defer in the stack. Stack, first in and then out, contrary to the queue, so execute defer3,defer2 in turn, and then return the result.

The 4.main function receives the return value of test_defer and starts to print the result.

Before the 5.main function ends, it executes the defer in this function, so it starts to execute defer1.

Is the result carried out like this? Let's take a look at the results. There's no difference.

Image.png

So there may be a little friend here who wants to ask, why is defer designed as a stack?

In a popular scene, defer does the clearing work, right? well, in such a scene, a thief goes to the warehouse to steal things, and when he has finished his work, he has to wipe the footprints, right? it is impossible for him to wipe the footprints from the position where he entered the door. He can only go back and wipe the footprints of the last step first. Moreover, often the last step is based on the previous basis, for example, the thief, he wants to steal the jewelry in the cabinet. Well, does he have to open the door first? when the thief does the cleaning work, it is impossible to close the door first and close the cabinet.

Defer is used to release resources, such as an operation, first lock the file, and then modify the file. When defer executes, it should close the file before releasing the lock. If you release the lock and then close the file, isn't that a mess? In terms of causality, the later allocated resources may depend on the previous resources, but the previous resources will certainly not rely on the later open resources. So it's not a problem to perform the shutdown the other way around.

Some people say, can't I just let defer come in and first out? Permission is allowed, but not advocated. Haha, do you feel the aura of teacher Luo Xiang? please take a look at the following code. If defer is nested, then defer will be executed from outside to inside, peeling onions one layer at a time.

Package mainfunc main () {defer func () {println ("I am defer1") defer func () {println ("I am defer2") defer func () {println ("I am defer3")} ()} () () panic ("i am panic")} panic

Panic often appears in pairs with recover, and also has a close relationship with defer. Panic can change the control flow of the program. After calling panic, it immediately stops executing the remaining code of the current function, and recursively executes the caller's defer in the current Goroutine.

Let's take a look at the following code

Package mainimport "time" func main () {defer func () {println ("i am main defer1")} () go func () {defer func () {println ("i am goroutine defer2")} () defer func () {println ("i am goroutine defer3")} () panic ("i am panic") defer func () {println ("i am goroutine defer4")} () time.Sleep (1 * time.Second)}

From the previous analysis, we know the following results

Defer1 stack

two。 Execute goroutine 3.defer2 stack 4.defer3 stack 5.panic interrupts program execution, executes defer3,defer2,panic in turn, but the programs after panic will no longer run, and the defer in main will not execute

Image.png

Why should I add time.Sleep if not?

As you can see from the screenshot, if there is no time.Sleep, it seems that the collaborative process has not been executed, why is this so? Because we know that the collaboration is not preemptive, if you delete the time.Sleep, the primary goroutine will not give up control of the secondary goroutine, but the goroutine must give up control in order to run another goroutine, and time.Sleep is a way to give up control. To put it simply, your program is occupied by the main master program from beginning to end, and the sub-program will not actively preempt the cpu, so the main program must take the initiative to give up the cpu so that the sub-program can be polled by cpu before it can be executed.

By the way, what is a cooperative journey?

Co-programming is one of the most important features of go language, so how do we understand it? Co-program, in short, is a lightweight thread, the size of a co-program is about 2k, which also explains why go can be millions on a single machine.

The creation of a go program in the go language is easy, just add a function call after the protocol keyword. Code lift chestnut

Package mainimport "time" func main () {println ("i am main goroutine") go func () {println ("I am goroutine_in_1") go func () {println ("I am goroutine_in_2") go func () {println ("I am goroutine_in_3")} ()} () time.Sleep (1*time.Second); println ("main goroutine is over")}

Image.png

How does the main function work? In fact, the main function also runs in goroutine, but it is the main co-program. We have nested several co-programs in the chestnut above, but there is no hierarchical relationship between them. There are only two kinds of co-programs, the sub-program and the main program. In the above code, we let the master program rest for a second and wait for the sub-program to return the result. If you do not let the main program rest for a second, that is, give up the cpu, there is no chance for the sub-program to be executed, because after the main program ends, no matter whether the sub-program is in any state, it will all die.

But in practical use, we should protect each sub-program and ensure their safe operation. The exception of a sub-program will propagate to the main program, which will directly cause the main program to fail and the program to crash. Like this chestnut below.

Package mainimport "time" func main () {println ("i am main goroutine") go func () {println ("I am goroutine_in_1") go func () {println ("I am goroutine_in_2") go func () {println ("I am goroutine_in_3") panic ("i am panic")} ()} () time.Sleep (1*time.Second); println ("main goroutine is over")}

The last sentence, main goroutine is over did not print, the program did not execute. We talked about that earlier. No matter what kind of program you are, I will stop the program when I encounter panic, even if it is a sub-program. All right, let's stop here. Let's move on to recover.

Recover

Recover can stop a program crash caused by panic, which is a function that can only play a role in defer. Does not work in other scopes. Why would you say that? Let's look at this chestnut below.

Package mainimport "fmt" func main () {defer func () {println ("i am main")} () if err: = recover (); err! = nil {fmt.Println (err)} panic ("i am panic")}

Take a look at the implementation result

Image.png

We see that when we encounter panic, we execute defer, then we execute panic, and we don't perform if conditional judgment. Why? Recover catches errors. There are no errors when running to if. What do you catch? When running to panic, if has already been executed, how to capture it? So someone might think, why don't I just put if behind panic? Okay? The answer is no, panic, as we have said before, no matter who you are, you have to stop when you see me. Let's go back to what we just said. The panic appears and the program stops executing, but the program will cycle through defer. If I catch errors in defer, will I be able to solve this problem? It can be seen that the designers of go have good intentions! Is there anyone here who will ask a question: defer can be nested, so can panic be nested? Of course, defer can hold everything, and panic can be nested in defer as well.

Package mainfunc main () {defer func () {defer func () {panic ("I am panic3")} () panic ("I am panic2")} () panic ("I am panic1")} () panic ("i am panic")}

Why do you execute the last line of panic before executing defer? this is a little different from what I said earlier when you encounter panic and execute defer first, right? but in your opinion, defer takes precedence over panic and defer+panic.

So now, let's write an example of how defer captures panic and resumes normal program execution.

Package mainimport "fmt" func main () {havePanic (); println ("i will go on")} func havePanic () {defer func () {if err:=recover (); err! = nil {fmt.Println ("i get a panic") fmt.Println (err)} () panic ("i am panic");}

Interpret the above program, execute havePanic, the first defer of havePanic to enter the stack, go down and encounter panic, you will first execute defer,defer which prints err information, and can do some other processing, such as logging, retry and so on. Then main continues to execute the following print to take a look at the execution result

Image.png

The following is to add a little more knowledge of the cooperative process.

Isn't go known as millions of collaborations? So let's really give it a million dollars to see if my computer can live in hold or not.

Come on! Write a piece of code

Package mainimport ("fmt"time") func main () {I: = 1; for {go func () {for {time.Sleep (time.Second)}} () if I > 1000000 {fmt.Printf ("I have started% d programs\ n", I)} else {fmt.Printf ("currently% d programs\ n", I)} iprograms +}}

Take a screenshot to take a look at my current machine status.

Screenshot after millions of collaborations are suspended

Image.png

Because the output could not keep up with the speed, in fact, in the end, I ran 1842504 trips.

Talk about the feeling after running: the fan whirled for about three minutes, and I calculated that a co-trip would probably look like 2.45kb.

Image.png

The difference between a cooperative program and a thread

Multiple threads can be run within a process, and each thread can run many collaborators. The thread is responsible for scheduling the co-program to ensure that each co-program has a chance to be executed. When a co-program sleeps, it gives up the running rights of the thread to other co-programs to run, and cannot continue to occupy the thread. At most one co-program is running within the same thread.

The scheduling of threads is carried out by the operating system, and the scheduling algorithm runs in kernel mode, while the call of the co-program is carried out by the runtime of Go language, and the scheduling algorithm runs in user mode.

The cooperative process can be simplified into three states, the running state, the ready state and the dormant state. There is at most one running co-program in the same thread. Ready-state co-programs refer to those that have the running ability but have not been given the opportunity to run. They can be scheduled to the running state at any time, and the dormant co-programs do not have the running ability. They are waiting for some conditions to occur, such as the completion of the IO operation, the end of sleep time, and so on.

The scheduling of threads by the operating system is preemptive, that is to say, the dead loop of a single thread will not affect the execution of other threads, and the continuous operation of each thread is limited by time slices.

The scheduling of cooperative programs by the Go language runtime is not preemptive. If a single co-program dominates the execution of a thread through a dead loop, then the thread has no chance to run other co-programs, and you can say that the thread faked its death. However, there are often multiple threads within a process, fake death of a thread is fine, all fake death will lead to the whole process stuck.

Each thread will contain multiple ready cooperators to form a ready queue. If the thread dies because of an individual dead loop, will all the ready collaborators in this queue have no chance to run? The Go Runtime Scheduler uses the work-stealing algorithm. When a thread is idle, that is, all the co-routines on that thread are dormant (or none), it will go to the ready queue of other threads and steal some co-routines to run. In other words, these threads will take the initiative to find work, and under normal circumstances, the runtime will try to distribute work tasks equally.

How many threads do I have?

By default, the Go runtime sets the number of threads to the number of machine CPU logical cores. At the same time, its built-in runtime package provides a GOMAXPROCS (n int) function that allows us to dynamically adjust the number of threads. Note that the name of this function is all uppercase. This function returns the number of threads before modification, if the parameter n

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

Development

Wechat

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

12
Report