Somethings about panic-recover mechanism in Golang

131 阅读1分钟

Unlike php and java, golang has no try-catch statement to process exceptions or errors, but it has panic-recover mechanism to achieve similar objective.
This article lists something critical about panic-recover for further reference.

  1. panic-recover has goroutine scope, meaning panic-recover in one goroutine does not affect that of another goroutine.
package main

import (
	"fmt"
	"sync"
)

func worker(wg *sync.WaitGroup) {
	defer wg.Done()
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recover from worker: ", r)
		}
	}()

	panic("worker")
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recover from main: ", r)
		}
	}()

	var wg sync.WaitGroup
	wg.Add(1)
	go worker(&wg)

	wg.Wait()
	fmt.Println("Done")
}

when panic in worker, the defered functions are called, with recover from worker: worker printed, then prints Done. The recover() in main does not affect worker()'s panic.
2) Although worker is paniced, wg.Done() is still needed, otherwise there will be a deadlock.
3) While the recover() call is missing from defer, the wg.Done() is called, then main goroutine returns from wg.Wait(), there is a Done printed, then panic: worker is generated by golang.

package main

import (
	"fmt"
	"sync"
)

func worker(wg *sync.WaitGroup) {
	defer wg.Done()

	panic("worker")
}

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recover from main: ", r)
		}
	}()

	var wg sync.WaitGroup
	wg.Add(1)
	go worker(&wg)

	wg.Wait()
	fmt.Println("Done")
}

with output in console as following,

Done
panic: worker

goroutine 18 [running]:
main.worker(0xc0000ae004)
        /Users/wangfeng14/go/goproject/src/main.go:11 +0x65
created by main.main
        /Users/wangfeng14/go/goproject/src/main.go:23 +0x91
exit status 2

indicating that in order to avoid program from crashing, we should call recover() in goroutine to process the unforseen exceptions or errors. And we should minimize the call of panic in our program, instead we can return a bool type to indicate an error unless we want to throw the execption to top level of the current goroutine.