1
0
mirror of https://github.com/charlienet/go-mixed.git synced 2025-07-17 16:12:42 +08:00
Files
go-mixed/panic/panic.go
2022-07-04 12:01:44 +08:00

82 lines
1.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package panic
import (
"context"
"fmt"
"runtime/debug"
)
type Panic struct {
R any
Stack []byte
}
func (p Panic) String() string {
return fmt.Sprintf("%v\n%s", p.R, p.Stack)
}
type PanicGroup struct {
panics chan Panic // 致命错误通知
dones chan int // 协程完成通知
jobs chan int // 并发数量
jobN int32 // 工作协程数量
}
func NewPanicGroup(maxConcurrent int) *PanicGroup {
return &PanicGroup{
panics: make(chan Panic, 8),
dones: make(chan int, 8),
jobs: make(chan int, maxConcurrent),
}
}
func (g *PanicGroup) Go(f func()) *PanicGroup {
g.jobN++
go func() {
g.jobs <- 1
defer func() {
<-g.jobs
// go 语言只能在自己的协程中捕获自己的 panic
// 如果不处理,整个*进程*都会退出
if r := recover(); r != nil {
g.panics <- Panic{R: r, Stack: debug.Stack()}
// 如果发生 panic 就不再通知 Wait() 已完成
// 不然就可能出现 g.jobN 为 0 但 g.panics 非空
// 的情况,此时 Wait() 方法需要在正常结束的分支
// 中再额外检查是否发生了 panic非常麻烦
return
}
g.dones <- 1
}()
f()
}()
return g
}
func (g *PanicGroup) Wait(ctx context.Context) error {
if g.jobN == 0 {
panic("no job to wait")
}
for {
select {
case <-g.dones: // 协程正常结束
g.jobN--
if g.jobN == 0 {
return nil
}
case p := <-g.panics: // 协程有 panic
panic(p)
case <-ctx.Done():
// 整个 ctx 结束,超时或者调用方主动取消
// 子协程应该共用该 ctx都会收到相同的结束信号
// 不需要在这里再去通知各协程结束(实现起来也麻烦)
return ctx.Err()
}
}
}