星河

醉后不知天在水,满船清梦压星河

Golang 协程更好的进行错误处理

mingzaily / 2022-06-02


利用 channel 来传输多个 goroutine 中的 errors

Go 的经典哲学:不要通过共享内存来通信,而是通过通信来实现内存共享

func main() {
	cherrors := make(chan error)
	wgDone := make(chan bool)

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		//... 业务逻辑
		wg.Done()
	}()
	go func() {
		//... 业务逻辑
		err := returnErr()
		if err != nil {
			cherrors <- err
		}
		wg.Done()
	}()
	go func() {
		wg.Wait()
		close(wgDone)
	}()

	select {
	case <-wgDone:
		break
	case err := <-cherrors:
		close(cherrors)
		fmt.Println(err)
	}

	time.Sleep(time.Second)
}

func returnErr() error {
	return errors.New("出错啦。。我是错误信息")
}

使用 sync/errgroup

使用官方提供的golang.org/x/sync/errgroup标准库

type Group
    func WithContext(ctx context.Context) (*Group, context.Context)
    func (g *Group) Go(f func() error)
    func (g *Group) Wait() error
func main() {
	group := new(errgroup.Group)

	nums := []int{-1, 0, 1}
	for _, num := range nums {
		num := num
		group.Go(func() error {
			res, err := output(num)
			fmt.Println(res)
			return err
		})
	}

	if err := group.Wait(); err != nil {
		fmt.Println("Get errors: ", err)
	} else {
		fmt.Println("Get all num successfully!")
	}
}

func output(num int) (int, error) {
	if num < 0 {
		return 0, errors.New("math: square root error!")
	}
	return num, nil
}

每启动一个新的 goroutine 都直接使用 Group.Go 方法,在等待和错误处理上使用 Group.Wait 方法。

这种方法进行错误处理的好处是不需要关注非业务逻辑的控制代码,比较简洁明了。