This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

Writing in the front

Learning Golang, there is still a long way to go, I still remember when I started learning Golang, it is really very simple to write, there are a lot of packages and tools to use, there is no need to repeat the wheel, but to really learn a language as a tool, for its principle is very necessary to learn to understand

Concurrent error

Golang is naturally high in concurrency, and when coding, goroutine can be abused. How can it be abused

func main(a) {
	for i := 0; i < 10; i++ {
		go func(a) {
			fmt.Println(" the num is ", i)
		}()
	}
	time.Sleep(time.Second)
	fmt.Println(" program over !! ")}Copy the code

XDM take a look at the simple program above and run go run main.go what is the output?

Does it print 0 through 9? Let’s actually see what happens

# go run main.go
 the num is  10
 the num is  10
 the num is  10
 the num is  10
 the num is  10
 the num is  10
 the num is  10
 the num is  10
 the num is  10
 the num is  10
 program over !!
Copy the code

Oh no, this is why, obviously the loop 10 times, should be increased by 1 each time to print the result

In fact, the phenomenon we see is a concurrency error

To solve the error

Let’s try passing in the parameter I to an anonymous function to see if it works better

func main(a) {
	for i := 0; i < 10; i++ {
		go func(i int) {
			fmt.Println(" the num is ", i)
		}(i)
	}
	time.Sleep(time.Second)
	fmt.Println(" program over !! ")}Copy the code

Run main.go again to see the output

# go run main.gothe num is 0 the num is 1 the num is 2 the num is 3 the num is 4 the num is 5 the num is 6 the num is 7 the num is 8 the  num is 9 program over !!Copy the code

Sure enough, that’s what we wanted

Then, looking back at the code carefully, we can find that I is a variable in the main coroutine, and the main coroutine will modify the value of the address of I. The address of variable I is used repeatedly all the time, but multiple sub-coroutines also keep reading the value of I, leading to concurrent errors

To avoid this concurrency error, you can use the pass parameter copy we used above

To explore the

Let’s confirm again whether the address of I is always the same

func main(a) {
	for i := 0; i < 10; i++ {
		fmt.Printf(" i = %d , &i = %p \n", i,&i)

		go func(a) {
			fmt.Printf(" the i = %d , &i = %p \n", i,&i)
		}()
	}
	time.Sleep(time.Second)
	fmt.Println(" program over !! ")}Copy the code

The program runs as follows: the primary and subcoroutines call I the same I at exactly the same address

Let’s look at how the address of I differs when dealing with concurrency errors

func main(a) {
	for i := 0; i < 10; i++ {
		fmt.Printf(" i = %d , &i = %p \n", i,&i)

		go func(i int) {
			fmt.Printf(" the i = %d , &i = %p \n", i,&i)
		}(i)
	}
	time.Sleep(time.Second)
	fmt.Println(" program over !! ")}Copy the code

We can see that the address of I in the main coroutine is still the same, which is true, but the I in the subcoroutine each of the coroutines has a different address of the I variable, and each of the coroutines outputs its own variable I, so there is no such error

Program crash Panic

Sometimes when we code, we will open up multiple coroutines, but we don’t deal with the possibility of panic in the coroutine, if the child coroutine dies, then the main coroutine will also die, so we need to pay special attention to that

Take a simple example

func main(a) {
	for i := 0; i < 5; i++ {

		go func(a) {
			a := 10
			b := 0
			fmt.Printf(" the i = %d \n", a/b)
		}()
	}
	time.Sleep(time.Second)
	fmt.Println(" program over !! ")}Copy the code

The above program is obviously for the cause of panic, is a division of 0 exception, can be expected, the whole program because of the subcoroutine panic and die

The following error message is displayed after running the program:

# go run main.go
panic: runtime error: integer divide by zero

goroutine 5 [running]:
main.main.func1()
        /home/admin/golang_study/later_learning/goroutine_test/main.go:24 +0x11
created by main.main
        /home/admin/golang_study/later_learning/goroutine_test/main.go:21 +0x42
exit status 2

Copy the code

Accession processing means

We’re going to do it before each subcoroutine exits, so panic of the subcoroutine doesn’t cause the main coroutine to die, so keep that in mind

func main(a) {
	for i := 0; i < 5; i++ {

		go func(a) {
			defer func(a) {
				if err := recover(a); err ! =nil {
					fmt.Println("recover one goroutine panic")
				}
			}()
			a := 10
			b := 0
			fmt.Printf(" the i = %d \n", a/b)
		}()
	}
	time.Sleep(time.Second)
	fmt.Println(" program over !! ")}Copy the code

The program runs as follows:

# go run main.go
recover one goroutine panic
recover one goroutine panic
recover one goroutine panic
recover one goroutine panic
recover one goroutine panic
 program over !!
Copy the code

It is obvious that there is no panic in the program, since panic in each subcoroutine is dealt with. We can also use the Runtime package provided by Golang to print out specific panic information for problem analysis

Let’s write a simple example

func main(a) {
	for i := 0; i < 5; i++ {

		go func(a) {
			defer func(a) {
				if err := recover(a); err ! =nil {
					buf := make([]byte.512)
					last := runtime.Stack(buf, false)
					fmt.Println("last == ",last)
					buf = buf[:last]
					fmt.Printf("[PANIC]%v\n%s\n", err, buf)
				}
			}()

			a := 10
			b := 0
			fmt.Printf(" the i = %d \n", a/b)
		}()
	}
	time.Sleep(time.Second)
	fmt.Println(" program over !! ")}Copy the code

Here we use runtime.stack (buf, false) to count the number of bytes of goroutine Panic Stack information and print it out

Let’s look at the effect first

We printed out the number of bytes of panic stack information, and we printed out the details of Panic as well. Most importantly, the program did not crash

By using the above method, we can make the subcoroutine panic not affect the main coroutine and print the stack information of the subcoroutine panic

Look at the source code

Look at the source code for this function to understand

Stack formats the Stack trace of the goroutine call as buF and returns the number of bytes written to buf.

If true, Stack formats all other Goroutine’s Stack traces into buF after the current Goroutine’s trace.

Golang has many other skills, and we need to use them to make it worthwhile

Welcome to like, follow and favorites

Friends, your support and encouragement, I insist on sharing, improve the quality of the power

All right, that’s it for this time

Technology is open, our mentality, should be more open. Embrace change, live in the sun, and strive to move forward.

I am Nezha, welcome to like, see you next time ~