错误示例
go
type chanData struct {
result string
error error
}
func Biz1() {
t := time.NewTimer(time.Second * 10)
ctx := context.Background()
ch := make(chan chanData)
go doChan(ctx, ch)
fmt.Println("Biz1 begin")
for {
select {
case <-t.C:
fmt.Println("Biz1 to time")
return
case data := <-ch:
fmt.Println("Biz1 data", jsonx.ToString(data.result), "Biz1 err", data.error.Error())
return
}
}
}
type RecoverHook func()
func Recover(ctx context.Context, method string, hooks ...RecoverHook) {
if err := recover(); err != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
fmt.Printf("panic err: %v\n%v\n end!!!\n", err, string(buf))
for _, hook := range hooks {
hook()
}
}
}
func doChan(ctx context.Context, ch chan chanData) {
fmt.Println("doChan begin")
defer Recover(ctx, "doChan", func() {
fmt.Println("Recover chan before")
ch <- chanData{error: errors.New("panic happened")}
fmt.Println("Recover chan after")
})
for i := 0; i < 12; i++ {
fmt.Printf("doChan i:%v\n", i)
time.Sleep(time.Second * 1)
}
panic("doChan panic")
fmt.Println("doChan end")
}
func TestDemo111(t *testing.T) {
f, err := os.Create("trace.out")
if err != nil {
panic(err)
}
defer f.Close()
err = trace.Start(f)
if err != nil {
panic(err)
}
defer trace.Stop()
go Biz1()
for i := 0; i < 20; i++ {
fmt.Printf("Biz i:%v\n", i)
time.Sleep(time.Second * 1)
}
}

协程被阻塞8秒
正确示例
go
type chanData struct {
result string
error error
}
type ChanDataWapper struct {
ch chan chanData
mu *sync.Mutex
closed bool
}
func NewChanInfo() *ChanDataWapper {
return &ChanDataWapper{
ch: make(chan chanData),
mu: &sync.Mutex{},
closed: false,
}
}
func Biz1() {
t := time.NewTimer(time.Second * 10)
ctx := context.Background()
dataChanWappter := NewChanInfo()
defer func() {
fmt.Println("Biz1 before close")
dataChanWappter.mu.Lock()
dataChanWappter.closed = true
dataChanWappter.mu.Unlock()
fmt.Println("Biz1 after close")
}()
go doChan(ctx, dataChanWappter)
fmt.Println("Biz1 begin")
for {
select {
case <-t.C:
fmt.Println("Biz1 to time")
return
case data := <-dataChanWappter.ch:
fmt.Println("Biz1 data", jsonx.ToString(data.result), "Biz1 err", data.error.Error())
return
}
}
}
type RecoverHook func()
func Recover(ctx context.Context, method string, hooks ...RecoverHook) {
if err := recover(); err != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
fmt.Printf("panic err: %v\n%v\n end!!!\n", err, string(buf))
for _, hook := range hooks {
hook()
}
}
}
func doChan(ctx context.Context, cw *ChanDataWapper) {
fmt.Println("doChan begin")
defer Recover(ctx, "doChan", func() {
cw.mu.Lock()
if !cw.closed {
fmt.Println("Recover chan before")
cw.ch <- chanData{error: errors.New("panic happened")}
fmt.Println("Recover chan after")
} else {
fmt.Println("Recover chan closed")
}
cw.mu.Unlock()
})
for i := 0; i < 12; i++ {
fmt.Printf("doChan i:%v\n", i)
time.Sleep(time.Second * 1)
}
panic("doChan panic")
fmt.Println("doChan end")
}
func TestDemo111(t *testing.T) {
f, err := os.Create("trace.out")
if err != nil {
panic(err)
}
defer f.Close()
err = trace.Start(f)
if err != nil {
panic(err)
}
defer trace.Stop()
go Biz1()
for i := 0; i < 20; i++ {
fmt.Printf("Biz i:%v\n", i)
time.Sleep(time.Second * 1)
}
}
go tool trace trace.out