本文主要研究一下gost的GoSafely
gost/runtime/goroutine.go
func GoSafely(wg *sync.WaitGroup, ignoreRecover bool, handler func(), catchFunc func(r interface{})) { if wg != nil { wg.Add(1) } go func() { defer func() { //...... }() handler() }() }
GoSafely接收WaitGroup、ignoreRecover、handler、catchFunc参数,其大致的模板是,首先对WaitGroup进行add(1),然后一步执行带defer的handler
gost/runtime/goroutine.go
defer func() { if r := recover(); r != nil { if !ignoreRecover { fmt.Fprintf(os.Stderr, "%s goroutine panic: %v\n%s\n", time.Now(), r, string(debug.Stack())) } if catchFunc != nil { //...... } } if wg != nil { wg.Done() } }()
GoSafely的defer先执行recover(),然后根据ignoreRecover判断是否打印err,最后处理WaitGroup,与普通recover不同的是多了一个catchFunc处理
gost/runtime/goroutine.go
if catchFunc != nil { if wg != nil { wg.Add(1) } go func() { defer func() { if p := recover(); p != nil { if !ignoreRecover { fmt.Fprintf(os.Stderr, "recover goroutine panic:%v\n%s\n", p, string(debug.Stack())) } } if wg != nil { wg.Done() } }() catchFunc(r) }() }
catchFunc算是一个mini版的GoSafely,先执行wg.Add(1),再异步执行func,异步func里头先注册defer,处理recover及wg,然后执行catchFunc
gost/runtime/goroutine_test.go
func TestGoSafe(t *testing.T) { times := int32(1) var wg sync.WaitGroup GoSafely(&wg, false, func() { panic("hello") }, func(r interface{}) { atomic.AddInt32(×, 1) }, ) wg.Wait() assert.True(t, atomic.LoadInt32(×) == 2) GoSafely(nil, false, func() { panic("hello") }, func(r interface{}) { atomic.AddInt32(×, 1) }, ) time.Sleep(1e9) assert.True(t, atomic.LoadInt32(×) == 3) }
这里模拟了一下handler产生panic,一个有WaitGroup,一个没有WaitGroup的场景
gost提供了GoSafely方法,它接收WaitGroup、ignoreRecover、handler、catchFunc参数,其大致的模板是,首先对WaitGroup进行add(1),然后一步执行带defer的handler。catchFunc算是一个mini版的GoSafely,先执行wg.Add(1),再异步执行func,异步func里头先注册defer,处理recover及wg,然后执行catchFunc。