关于协程的使用问题

来源:6-1 channel

burningx

2021-09-29 19:25:19

老师好,我在写一个功能,是获取用户的一些统计信息,分别有提问总数点赞总数回答总数

我的想法是开3个协程,分别请求三个方法获取对应的统计数据,下面是我的实现代码,数据是能正确获取,但是我总觉得这么写的代码怪怪的,请教老师,我这么实现合理吗?或者有其他更优的方法实现?

func (s *Service) doGetTotalByUser(c chan fields.Total, f func() fields.Total) {
	defer close(c)
	total := f()
	c <- total
}

func (s *Service) GetStatisticByUser(c context.Context, req *qapb.StatisticByUserRequest) (*qapb.StatisticByUserResponse, error) {
	userID := uint32(req.UserId)
	postTotalChan := make(chan fields.Total)
	replyTotalChan := make(chan fields.Total)
	praiseTotalChan := make(chan fields.Total)

	go s.doGetTotalByUser(postTotalChan, func() fields.Total {
		postTotal, err := s.Mysql.GetPostTotalByUser(c, userID)
		if err != nil {
			zap.S().Error("无法获取用户问题总数: %v", err)
		}
		return postTotal
	})

	go s.doGetTotalByUser(replyTotalChan, func() fields.Total {
		replyTotal, err := s.Mysql.GetReplyTotalByUser(c, userID)
		if err != nil {
			zap.S().Errorf("无法获取用户回复总数: %v", err)
		}
		return replyTotal
	})

	go s.doGetTotalByUser(praiseTotalChan, func() fields.Total {
		praiseTotal, err := s.Mysql.GetPraiseTotalByUser(c, userID)
		if err != nil {
			zap.S().Errorf("无法获取用户被点赞总数: %v", err)
		}
		return praiseTotal
	})

	postTotal := <-postTotalChan
	replyTotal := <-replyTotalChan
	praiseTotal := <-praiseTotalChan

	response := &qapb.StatisticByUserResponse{
		PostTotal:   postTotal.Int(),
		ReplyTotal:  replyTotal.Int(),
		PraiseTotal: praiseTotal.Int(),
	}
	return response, nil
}
写回答

1回答

ccmouse

2021-10-06

总体框架没什么问题。不过错误处理流程不太顺利。


我们很可能希望把错误通过response返回出去,而不是服务器端打一个日志然后静默的返回0。这里可以考虑把fields.Total和error合起来,做一个struct,通过channel把这个struct返回出去。那么构造response的时候就可以明确地说到底哪个值出错了。


​还有个小改进,其实doGetTotalByUser函数不需要,各goroutine直接把数据通过channel送出去即可。doGetTotalByUser的存在就是为了close channel。但是channel其实并不总是需要close的。channel在垃圾回收的时候资源就会被释放,close只是一个特殊的信号,通知接收方我不再发送数据。在我们这里很明确一共就一个数据,所以不需要close。


1
hurningx
hp>非常感谢CC老师的解答,对我帮助很大。

h021-10-08
共1条回复

0 学习 · 1399 问题

查看课程