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

相关推荐
你的人类朋友4 分钟前
✍️Node.js CMS框架概述:Directus与Strapi详解
javascript·后端·node.js
5:006 分钟前
云备份项目
linux·开发语言·c++
面朝大海,春不暖,花不开22 分钟前
自定义Spring Boot Starter的全面指南
java·spring boot·后端
笨笨马甲43 分钟前
Qt Quick模块功能及架构
开发语言·qt
钡铼技术ARM工业边缘计算机1 小时前
【成本降40%·性能翻倍】RK3588边缘控制器在安防联动系统的升级路径
后端
夜晚回家1 小时前
「Java基本语法」代码格式与注释规范
java·开发语言
YYDS3141 小时前
C++动态规划-01背包
开发语言·c++·动态规划
前端页面仔1 小时前
易语言是什么?易语言能做什么?
开发语言·安全
CryptoPP2 小时前
使用WebSocket实时获取印度股票数据源(无调用次数限制)实战
后端·python·websocket·网络协议·区块链
树叶@2 小时前
Python数据分析7
开发语言·python