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

相关推荐
葫芦和十三6 小时前
图解 MongoDB 26|片键设计:决定集群命运的一个决定
后端·mongodb·agent
Avan_菜菜7 小时前
使用 Docker + rclone 自建 WebDAV
后端·agent·claude
阳光是sunny8 小时前
别再被 worktree 绕晕了!AI 编程时代你必须掌握的 Git 隔离神器
前端·人工智能·后端
万少10 小时前
万少的博客 - 技术分享与解决方案
前端·javascript·后端
咖啡八杯10 小时前
GoF设计模式——备忘录模式
java·后端·spring·设计模式
苍何10 小时前
腾讯再放大招,企微 Agent 大圆开启内测
后端
ethantan10 小时前
一篇讲解AI Agent 组成:像人一样思考的智能体
人工智能·后端·程序员
apocelipes12 小时前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
Cosolar12 小时前
vLLM 生产级部署完全指南
人工智能·后端·架构
IT_陈寒13 小时前
垃圾回收器选错了,我的Java服务内存炸了
前端·人工智能·后端