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

/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

/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.