• go goroutine 怎样更好的进行错误处理


    前言

    在 Go 语言程序开发中,goroutine 的使用是比较频繁的,因此在日常编码的时候 goroutine 里的错误处理,怎么做会比较好呢?

    一般我们的业务代码如下:

    func main() {
    	var wg sync.WaitGroup
    	wg.Add(2)
    	go func() {
    		//... 业务逻辑
    		wg.Done()
    	}()
    	go func() {
    		//... 业务逻辑
    		wg.Done()
    	}()
    	wg.Wait()
    }
    

    在上面的代码中,我们运行了多个 goroutine,每个协程又是单独行动的,想要抛出 error 错误信息,也不怎么明智。

    通过错误日志记录

    常用的第一种方法:通过把错误记录写入到日志文件中,再结合相关的 logtail 进行采集和梳理。

    但这又会引入新的问题,那就是调用错误日志的方法写的到处都是,代码结构也比较乱、不直观。

    最重要的是无法针对 error 做特定的逻辑处理和流转。

    利用 channel 传输

    大家可能会想到 Go 的经典哲学:不要通过共享内存来通信,而是通过通信来实现内存共享(Do not communicate by sharing memory; instead, share memory by communicating)。

    第二种方法:利用 channel 来传输多个 goroutine 中的 errors:

    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("出错啦。。我是错误信息")
    }
    

    虽然使用 channel 后已经方便了不少,但编写 channel 还要关心一些非业务向的逻辑。

    使用 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
    
    • Go:启动一个协程,在新的 goroutine 中调用给定的函数。
    • Wait:等待协程结束,直到 Go 方法中的所有函数调用都返回,然后返回其中第一个非零错误(如果有错误的话)。

    结合其特性能够非常便捷的针对多 goroutine 进行错误处理:

    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 方法。

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

    总结

    在 Go 语言中,goroutine 是一种常用的方法,为此我们需要更了解 goroutine 的一系列相关知识,像是 context、error处理等

    在团队开发中,统一一定的规范,这样的代码阅读起来就会比较明朗,一些隐藏很深的 Bug 也会减少很多。

  • 相关阅读:
    每日一水 POJ8道水题
    编译和使用 MySQL C++ Connector
    j2ee model1模型完成分页逻辑的实现 详解!
    DB查询分析器访问EXCEL时,要在表名前后加上中括弧或双引号
    指向结构体变量的指针
    EOSS V3.0 企业运营支撑系统(基于RBAC原理的权限管理)
    MybatisGen1.0 Mybatis JavaBean Mapper生成工具
    The table name must be enclosed in double quotation marks or sqare bracket while accessing EXCEL by
    资源-Android:Android
    软件-开发软件:Android Studio
  • 原文地址:https://www.cnblogs.com/niuben/p/14995106.html
Copyright © 2020-2023  润新知