There is no problem not worth solving, and no technology not worth learning!

Panic capture and recovery mechanisms for goroutine in Go language

运行结果:如图1

Practical Go Concurrency Step by Step

其中一次执行结果输出了 hello,world 11 。小概率事件,或者说 执行结果大概率不会输出 “hello,world 12”及以后的输出。以下是一个简单的执行示意图1

(1) GoRoutine Quick Start: Multi-Scenario Execution Process Analysis (with Code + Flowchart)

运行后,控制台要么打印空结果,要么只打印少量结果,大部分阶乘结果丢失。

(2) Go Concurrency Learning: The Necessity of a Channel from Factorial Cases

测试代码(故意混用类型,验证报错) 测试结果(编译报错):如图1

(3) Go study notes: channel basic usage and 4 core considerations (with code test)

我运行了上述代码,整理出核心运行结果(截取部分),方便自己后续回顾:如图1

(4) GO Review Notes: Goroutine and Channel work together (solve the hidden danger of locking and sleeping)

Go language channel blocking depth resolution: from DeadLock to asynchronous processing

(5) Go language channel blocking depth resolution: from DeadLock to asynchronous processing

In my test environment (2-core CPU), calculating prime numbers from 1 to 200,000 yielded the following results (as shown in Figure 1): - Traditional serial implementation: Approximately 25 seconds. - Concurrent implementation using multiple coroutines: Approximately 12 seconds.

(6) Go language concurrency actual combat: optimization comparison of prime statistical performance (“traditional serial implementation” and “multi-processing concurrency implementation”)

运行结果:如图1

(7) Go language review: use select to solve the problem of blocking in the channel

运行结果:如图1

(8) Panic capture and recovery mechanisms for goroutine in Go language

In the concurrent programming of Go language, goroutine is a lightweight thread implementation. However, if panic occurs in goroutine and is not captured, the entire program will crash. In this article, we will dive into two cases of using and not using Recover in depth through specific examples, and we will dive into how to properly handle Panic in Goroutine.

problem background

Consider the following code scenario:

package main

import (
	"fmt"
	"time"
)

func sayHello() {
	for i := 0; i < 10; i++ {
		time.Sleep(time.Second)
		fmt.Println("hello,world")
	}
}

func test() {
	var myMap map[int]string
	myMap[0] = "golang" // 这里会触发 panic: assignment to entry in nil map
}

func main() {
	go sayHello()
	go test()

	for i := 0; i < 10; i++ {
		fmt.Println("main() ok=", i)
		time.Sleep(time.Second)
	}
}

In this example, the test() function attempts to write data to an uninitialized map, which triggers panic. Let’s analyze the two situations separately.

Case 1: Do not use Recover (comment out Defer func)

When the panic in the test() function is not captured:

executive process

1. The main function starts two goroutines: sayHello() and test()

2. SayHello() executes normally, prints ‘Hello, world’ every second

3. test() immediately trigger panic: assignment to entry in nil map

4. The entire program crashes and exits, including the main goroutine and all other goroutines

Running result: as shown in Figure 1

Running result: as shown in Figure 1
/app/go-atguigu/channel-details02 # go run main.go 
main() ok= 0
panic: assignment to entry in nil map

goroutine 20 [running]:
main.test()
        /app/go-atguigu/channel-details02/main.go:17 +0x1e
created by main.main in goroutine 1
        /app/go-atguigu/channel-details02/main.go:22 +0x2a
exit status 2

problem analysis

– The whole process will be terminated once the uncaught panic occurs in a goroutine

– Even if other goroutines (such as sayhello()) are running fine, they will be forced to stop

– The loop of the main thread cannot continue to execute

– This is catastrophic in the production environment and may cause the service to be unavailable

Case 2: Use Recover (Enable Defer Func)

By adding the defer + recovery mechanism in the [test() function:

func test() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("test() 发生错误:", err)
		}
	}()
	
	var myMap map[int]string
	myMap[0] = "golang" // 触发 panic,但会被 recover 捕获
}

executive process

1. The main function starts two goroutines: sayHello() and [test())

2. test() triggers panic, but is captured by recover()

3. Recover() returns the value of panic, and the program continues to execute

4. sayHello() runs normally, not affected

5. The loop of the main thread is also executed normally

Running result: as shown in Figure 2

Running result: as shown in Figure 2
/app/go-atguigu/channel-details02 # go run main.go 
main() ok= 0
test() 发生错误: assignment to entry in nil map
main() ok= 1
hello,world
main() ok= 2
hello,world
main() ok= 3
hello,world
main() ok= 4
hello,world
main() ok= 5
hello,world
main() ok= 6
hello,world
main() ok= 7
hello,world
main() ok= 8
hello,world
main() ok= 9
hello,world
/app/go-atguigu/channel-details02 # 

advantage analysis

– Isolation failure: Panic of a single goroutine will not affect other goroutines

– Program Stability: Main threads and other coroutines can continue to run normally

– Error handling: error logs can be logged for easy follow-up troubleshooting

– Elegant downgrade: Core features can continue to use even if some features fail

How the Recover mechanism works

the role of defer

The defer statement delays the function until the current function returns. The code in defer will execute whether the function returns normally or exits due to panic.

role of recovery

Recover() is a built-in function that can only be called efficiently in the defer function:

– If recover() is called in a normal execution flow, it returns nil

– If the current goroutine is panic, recover() will capture the value of the panic and stop the panic propagation

– After capture, the program will continue execution from where the panic occurs (actually returns after completing the remaining part of the current function)

key point

1. Must be used in defer: call recovery() directly in normal code to invalidate

2. Only valid in the current goroutine: each goroutine needs its own recovery mechanism

3. Can only be captured once: once the recovery captures the panic, the panic is processed

4. Does not affect other goroutines: one panic of goroutine will not automatically spread to other goroutines

best practice

1. Add Recover for important goroutines

func safeGoroutine(fn func()) {
	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Printf("goroutine panic: %v\n", err)
				// 可以记录日志、发送监控告警等
			}
		}()
		fn()
	}()
}

2. Applications in Web Server

func handler(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("handler panic: %v", err)
			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
		}
	}()
	
	// 处理请求的逻辑
}

3. Avoid overuse

– Don’t abuse Recover to cover up real bugs

– Errors should be caught and handled at the appropriate level

– For foreseeable errors, the Error return mechanism is preferred instead of Panic

Summary

In the concurrent programming of the Go language, the correct use of the Recover mechanism is the key to ensuring program stability:

– Not using Recover: Panic of a single goroutine will crash the entire program, affecting all running coroutines

– Use Recover: Isolate faults and ensure the normal execution of other goroutines and main threads

It is recommended to add the Recover mechanism in all booted goroutines, especially in the production environment. This will not affect the availability of the entire service even if there is a problem with a coroutine. At the same time, it is necessary to reasonably record and analyze the captured Panic information, and timely detect and repair potential problems.

Go language review: use select to solve the problem of blocking in the channel

Go / Gin Backend API Maintenance & Remote Technical Support

If your Go / Gin backend API, small service, or internal system needs maintenance, bug fixing, troubleshooting, performance optimization, or small feature iterations, feel free to contact me.

Ideal For:
✅ Go / Gin APIs with errors or slow response times
✅ Existing Go services that need long-term maintenance
✅ Backend APIs requiring small feature iterations
✅ Scheduled tasks or data processing services with issues
✅ MySQL / Redis / Nginx related troubleshooting
✅ Remote part-time technical support

We can start with a small task:
✅ API issue troubleshooting
✅ Log analysis
✅ Performance bottleneck diagnosis
✅ Deployment environment inspection
✅ Small feature assessment and iteration

Please contact me and mention: Go / Gin Maintenance Consultation.

Contact Me:
Telegram: @shuijingwan
WeChat: 13980074657
Email: shuijingwanwq@gmail.com

评论

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.