go~协程阻塞分析

错误示例

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

相关推荐
东鹏特饮2 分钟前
系统运维(传统/通用运维)面试题
开发语言·php
风若飞10 分钟前
Spring Ai 入门遇到问题1
java·后端·spring
今天要早睡_28 分钟前
C++01_入门基础复习
开发语言·c++
AI入职记1 小时前
Spring AI 结构化输出:结构 100% 通过,严格准确率只有 57%
后端·ai编程
aZhe的全栈知识分享1 小时前
AI 还原 Figma 效果总达不到预期?先别急着让它写代码
前端·后端·面试
码事漫谈1 小时前
WorkBuddy 一直更新卡死?两个毛病,一套处理办法
后端
函数小陈1 小时前
阿里云部署DSH与RAM完成运维
前端·后端
aurora_zjj1 小时前
一次持续 3 年的 OpenResty 死循环排查:最终定位到 LuaJIT ARM64 的一条汇编指令
后端
智驭未来掌门人1 小时前
动态平台雷达目标引导系统设计与实现
后端
Bazingga1 小时前
一次Agent超时熔断设计踩过的坑
后端