go goroutine 怎样进行错误处理

目录
  • 前言
  • 通过错误日志记录
  • 利用 channel 传输
  • 使用 sync/errgroup
  • 总结

前言

在 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 也会减少很多。

到此这篇关于go goroutine 怎样进行错误处理的文章就介绍到这了,更多相关go goroutine 错误处理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Go语言的错误处理和资源管理

    目录 一.defer 二.错误处理 三.统一错误处理的逻辑 四.panic 五.recover 六.error vs panic 七.错误处理综合示例 一.defer 1. defer保证在函数结束时发生. 2. defer列表为先进后出 3. 参数在defer语句时计算. 下面来看一个例子: 写入文件 package main import ( "aaa/functional/fbi" "bufio" "fmt" "os"

  • GO语言标准错误处理机制error用法实例

    本文实例讲述了GO语言标准错误处理机制error用法.分享给大家供大家参考.具体分析如下: 在 Golang 中,错误处理机制一般是函数返回时使用的,是对外的接口,而异常处理机制 panic-recover 一般用在函数内部. error 类型介绍 error 类型实际上是抽象了 Error() 方法的 error 接口,Golang 使用该接口进行标准的错误处理. 复制代码 代码如下: type error interface {  Error() string } 一般情况下,如果函数需要返

  • 详解Go多协程并发环境下的错误处理

    引言 在Go语言中,我们通常会用到panic和recover来抛出错误和捕获错误,这一对操作在单协程环境下我们正常用就好了,并不会踩到什么坑.但是在多协程并发环境下,我们常常会碰到以下两个问题.假设我们现在有2个协程,我们叫它们协程A和B好了: 如果协程A发生了panic,协程B是否会因为协程A的panic而挂掉? 如果协程A发生了panic,协程B是否能用recover捕获到协程A的panic? 答案分别是:会.不能. 那么下面我们来一一验证,并给出在具体的业务场景下的最佳实践. 问题一 如果

  • Go语言中更优雅的错误处理

    从现状谈起 Go语言受到诟病最多的一项就是其错误处理机制.如果显式地检查和处理每个error,这恐怕的确会让人望而却步.下面我们将给大家介绍Go语言中如何更优雅的错误处理. Golang 中的错误处理原则,开发者曾经之前专门发布了几篇文章( Error handling and Go和 Defer, Panic, and Recover.Errors are values )介绍.分别介绍了 Golang 中处理一般预知到的错误与遇到崩溃时的错误处理机制. 一般情况下,我们还是以官方博客中的错误

  • Golang try catch与错误处理的实现

    目录 Golang try catch panic panic主要使用场景: recover error 预定义错误值 自定义错误类型 使用 panic和recover模拟 tyr catch  谨慎! Golang try catch 虽然在使用Golang的时候发现没有try catch这种错误处理机制但是想一想golang作为一门优雅的语言,似乎也是情理之中.因为在java中 throws在函数签名中有一个关键字,旨在使异常流程比较简洁,但是一旦预期的异常数量增加,就很难准确捕捉到具体异常

  • Golang巧用defer进行错误处理的方法

    本文主要跟大家介绍了Golang巧用defer进行错误处理的相关内容,分享出来供大家参考学习,下面来看看详细的介绍: 问题引入 毫无疑问,错误处理是程序的重要组成部分,有效且优雅的处理错误是大多数程序员的追求.很多程序员都有C/C++的编程背景,Golang的程序员也不例外,他们处理错误有意无意的带着C/C++的烙印. 我们看看下面的例子,就有一种似曾相识的赶脚,代码如下: func deferDemo() error { err := createResource1() if err != n

  • Golang中重复错误处理的优化方法

    Golang 错误处理最让人头疼的问题就是代码里充斥着「if err != nil」,它们破坏了代码的可读性,本文收集了几个例子,让大家明白如何优化此类问题. 让我们看看 Errors are values中提到的一个 io.Writer 例子: _, err = fd.Write(p0[a:b]) if err != nil { return err } _, err = fd.Write(p1[c:d]) if err != nil { return err } _, err = fd.Wr

  • Go语言中错误处理实例分析

    本文实例讲述了Go语言中错误处理的方法.分享给大家供大家参考.具体分析如下: 错误是可以用字符串描述自己的任何东西. 主要思路是由预定义的内建接口类型 error,和其返回返回字符串窜的方法 Error 构成. type error interface { Error() string } 当用 fmt 包的多种不同的打印函数输出一个 error 时,会自动的调用该方法. 复制代码 代码如下: package main import (     "fmt"     "time

  • go goroutine 怎样进行错误处理

    目录 前言 通过错误日志记录 利用 channel 传输 使用 sync/errgroup 总结 前言 在 Go 语言程序开发中,goroutine 的使用是比较频繁的,因此在日常编码的时候 goroutine 里的错误处理,怎么做会比较好呢? 一般我们的业务代码如下: func main() { var wg sync.WaitGroup wg.Add(2) go func() { //... 业务逻辑 wg.Done() }() go func() { //... 业务逻辑 wg.Done(

  • golang中context的作用详解

    当一个goroutine可以启动其他goroutine,而这些goroutine可以启动其他goroutine,依此类推,则第一个goroutine应该能够向所有其它goroutine发送取消信号. 上下文包的唯一目的是在goroutine之间执行取消信号,而不管它们如何生成.上下文的接口定义为: type Context interface { Deadline() (deadline time.Time, ok bool) Done() <- chan struct{} Err() erro

  • Go语言实现的可读性更高的并发神库详解

    目录 前言 WaitGroup的封装 worker池 Stream ForEach和map ForEach map 总结 前言 前几天逛github发现了一个有趣的并发库-conc,其目标是: 更难出现goroutine泄漏 处理panic更友好 并发代码可读性高 从简介上看主要封装功能如下: 对waitGroup进行封装,避免了产生大量重复代码,并且也封装recover,安全性更高 提供panics.Catcher封装recover逻辑,统一捕获panic,打印调用栈一些信息 提供一个并发执行

  • 一文带你了解Go语言实现的并发神库conc

    目录 前言 worker池 Stream ForEach和map ForEach map 总结 前言 哈喽,大家好,我是asong:前几天逛github发现了一个有趣的并发库-conc,其目标是: 更难出现goroutine泄漏 处理panic更友好 并发代码可读性高 从简介上看主要封装功能如下: 对waitGroup进行封装,避免了产生大量重复代码,并且也封装recover,安全性更高 提供panics.Catcher封装recover逻辑,统一捕获panic,打印调用栈一些信息 提供一个并发

  • 关于Golang中for-loop与goroutine的问题详解

    背景 最近在学习MIT的分布式课程6.824的过程中,使用Go实现Raft协议时遇到了一些问题.分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 参见如下代码: for i := 0; i < len(rf.peers); i++ { DPrintf("i = %d", i) if i == rf.me { DPrintf("skipping myself #%d", rf.me) continue } go func() { DPrintf(

  • Golang常见错误之值拷贝和for循环中的单一变量详解

    前言 golang(中文名:go语言)是谷歌2009发布的第二款开源编程语言.Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全.支持并行进程..如果你想知道得更多,请移步至官网golang官网 在 Go 中函数的调用是值拷贝 copy value,而且在 for 循环中 v 的变量始终是一个变量.如果 v 是 pointer,print 这个 method 接收的是指针的拷贝,for 循环体中每次迭代 v 的 pointer va

  • Go 防止 goroutine 泄露的方法

    概述 Go 的并发模型与其他语言不同,虽说它简化了并发程序的开发难度,但如果不了解使用方法,常常会遇到 goroutine 泄露的问题.虽然 goroutine 是轻量级的线程,占用资源很少,但如果一直得不到释放并且还在不断创建新协程,毫无疑问是有问题的,并且是要在程序运行几天,甚至更长的时间才能发现的问题. 对于上面描述的问题,我觉得可以从两方面入手解决,如下: 一是预防,要做到预防,我们就需要了解什么样的代码会产生泄露,以及了解如何写出正确的代码: 二是监控,虽说预防减少了泄露产生的概率,但

  • go获取协程(goroutine)号的实例

    我就废话不多说了,大家还是直接看代码吧~ func GetGID() uint64 { b := make([]byte, 64) b = b[:runtime.Stack(b, false)] b = bytes.TrimPrefix(b, []byte("goroutine ")) b = b[:bytes.IndexByte(b, ' ')] n, _ := strconv.ParseUint(string(b), 10, 64) return n } 补充:Go语言并发协程Go

  • golang gin 框架 异步同步 goroutine 并发操作

    goroutine机制可以方便地实现异步处理 package main import ( "log" "time" "github.com/gin-gonic/gin" ) func main() { // 1.创建路由 // 默认使用了2个中间件Logger(), Recovery() r := gin.Default() // 1.异步 r.GET("/long_async", func(c *gin.Context) {

随机推荐