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

相关推荐
yang_love101142 分钟前
Spring Boot 中的 @ConditionalOnBean 注解详解
java·spring boot·后端
hhw1991121 小时前
c#知识点补充3
开发语言·c#
Antonio9151 小时前
【Q&A】观察者模式在QT有哪些应用?
开发语言·qt·观察者模式
Pandaconda1 小时前
【后端开发面试题】每日 3 题(二十)
开发语言·分布式·后端·面试·消息队列·熔断·服务限流
mqwguardain2 小时前
python常见反爬思路详解
开发语言·python
鱼樱前端2 小时前
mysql事务、行锁、jdbc事务、数据库连接池
java·后端
lmy3477712322 小时前
东软鸿蒙C++开发面经
开发语言·c++
Adellle2 小时前
MySQL
数据库·后端·mysql
hakesashou2 小时前
python多线程和多进程的区别有哪些
java·开发语言·jvm
每次的天空3 小时前
项目总结:GetX + Kotlin 协程实现跨端音乐播放实时同步
android·开发语言·kotlin