Skip to content

组件并发模式会出现 go协程阻塞积压 #82

@LittleBeeMark

Description

@LittleBeeMark

// Do 执行子组件
// ctx 业务上下文
// currentConponent 当前组件
// wg 父组件的waitgroup对象
func (bc *BaseConcurrencyComponent) Do(ctx *Context, currentConponent Component, wg *sync.WaitGroup) (err error) {
defer wg.Done()
// 初始化并发子组件channel
if bc.logicResChan == nil {
bc.logicResChan = make(chan interface{}, 1)
}

go currentConponent.BusinessLogicDo(bc.logicResChan)

select {
// 等待业务执行结果
case <-bc.logicResChan:
	// 业务执行结果
	fmt.Println(runFuncName(), "bc.BusinessLogicDo wait.done...")
	break
// 超时等待
case <-ctx.TimeoutCtx.Done():
	// 超时退出
	fmt.Println(runFuncName(), "bc.BusinessLogicDo timeout...")
	bc.Err = ErrConcurrencyComponentTimeout
	break
}
// 执行子组件
err = currentConponent.ChildsDo(ctx)
return

}

这个组件的并发设计中,子任务的执行是同时起生产者和消费者的,在消费者超时退出后,生产者(urrentConponent.BusinessLogicDo(bc.logicResChan))如果此时才获取到结果发送到消费者将会阻塞,这样会造成多go程阻塞,go程积压造成内存爆表。

我模仿写了一个伪代码:
func run(wg *sync.WaitGroup) {
defer wg.Done()
ch := make(chan string)

wg.Add(1)
go func() {
	time.Sleep(9 * time.Second)
            // 阻塞后死锁
	ch <- "work1"
}()

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

select {
case res := <-ch:
	fmt.Println("接收到任务", res)

case <-ctx.Done():
	fmt.Println("ctx err : ", ctx.Err())
	return
}

}

func main() {
wg := sync.WaitGroup{}
wg.Add(1)
go run(&wg)
wg.Wait()

fmt.Println("success")

}

运行结果是go程阻塞死锁 :
ctx err : context deadline exceeded
fatal error: all goroutines are asleep - deadlock!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions