原文:https://medium.com/a-journey-with-go/go-multiple-errors-management-a67477628cf1

​ 关于开发者使用Go遇到的最大挑战的年度调查报告中,错误管理是经常被争论和反复提起的话题。然而,当涉及到在并发环境中处理错误或为相同的 goroutine 组合多个错误时,Go 提供了很好的包,使多个错误的管理变得很容易

单个 goroutine,多个错误

例如,当您处理具有重试策略的代码时,将多个错误合并成一个非常有用。下面是一个基本的例子,其中我们需要收集生成的错误:

var data = []byte(
`a,b,c
foo
1,2,3
,",
`) func main() {
reader := csv.NewReader(bytes.NewBuffer(data))
for {
if _, err := reader.Read(); err != nil {
if err == io.EOF {
break
}
log.Printf(err.Error())
}
}
}

上面的程序读取、解析一个 CSV 文件,并且显示报错信息。它可以更方便的对错误进行分组以获得完整的报告。为了将错误合并成一个,我们需要在两个很优秀的包中选择一个:

  • 使用 HashiCorp 的 go-multierror ,可以将错误合并为一个标准错误

    func main() {
    var errs error
    reader := csv.NewReader(bytes.NewBuffer(data))
    for {
    if _, err := reader.Read(); err != nil {
    if err == io.EOF {
    break
    }
    errs = multierror.Append(errs, err)
    }
    }
    if errs != nil {
    log.Printf(errs.Error())
    }
    }

    接着错误报告可以被打印出来

  • 使用 Uber 的 multierr

    代码的实现是类似的,下面是输出

    错误通过分号连接,没有任何其他格式。

    关于每个包的性能,下面是一个具有较高失败次数的程序的基准测试

    name                    time/op         alloc/op        allocs/op
    HashiCorpMultiErrors-4 6.01µs ± 1% 6.78kB ± 0% 77.0 ± 0%
    UberMultiErrors-4 9.26µs ± 1% 10.3kB ± 0% 126 ± 0%

    Uber 的速度稍慢,占用内存更多。但是,这个包的设计目的是在收集到错误之后将其分组,而不是每次都添加错误。当对错误进行分组时,结果很接近,但是代码不够优雅,因为它需要额外的步骤。以下是新的测试结果:

    name                    time/op         alloc/op        allocs/op
    HashiCorpMultiErrors-4 6.01µs ± 1% 6.78kB ± 0% 77.0 ± 0%
    UberMultiErrors-4 6.02µs ± 1% 7.06kB ± 0% 77.0 ± 0%

    这两个包都利用了 Go 错误接口,它们都实现了自己的 Error() string 函数。

一个错误, 多个 goroutines

当多个 goroutine 来处理一个任务时,正确管理结果和聚合错误以确保程序的正确性是必要的。

让我们从一个使用多个 goroutine 执行一系列操作的程序开始,每一个操作都持续一秒:

func main() {
var wg sync.WaitGroup
for i := 0; i < 4; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if err := action(); err != nil {
return
}
if err := action(); err != nil {
return
}
if err := action(); err != nil {
return
}
}()
}
wg.Wait()
}

为了解释错误的传播,第三个 goroutine 的第一个操作会失败,下面时正在发生的事情:

正如预期的那样,程序大约需要 3 秒钟,因为大多数 goroutine 需要经历三个操作,每个操作花费一秒:

go run .  0.30s user 0.19s system 14% cpu 3.274 total

然而,我们可能希望让这些 goroutine 互相依赖,并在其中一个失败时取消它们以避免不必要的工作,解决方案是添加一个上下文,一旦 goroutine 失败,它就会取消它。

这个功能这是 errgroup 包所提供的;当一组 goroutine 工作时的错误和上下文传播。

下面时使用 errgroup包的代码:

func main() {
g, ctx := errgroup.WithContext(context.Background())
for i := 0; i < 4; i++ {
g.Go(func () error {
if err := action(ctx); err != nil {
return err
}
if err := action(ctx); err != nil {
return err
}
if err := action(ctx); err != nil {
return err
}
return nil
})
}
if err := g.Wait(); err != nil {
log.Printf(err.Error())
}
}

现在程序运行的更快了,因为它在发生错误时传播了取消上下文:

go run .  0.30s user 0.19s system 38% cpu 1.269 total

该包提供的另一个好处是,我们不再需要担心 goroutine 的添加和对 goroutine 标记完成。包替我们管理这些, 我们只需要等待程序完成并结束。

【译】GO语言:管理多个错误的更多相关文章

  1. Module Zero之语言管理

    返回<Module Zero学习目录> 概览介绍 如何开启 管理语言 管理本地化文本 概览介绍 ABP定义了一个健壮的UI本地化系统,它可用于服务端和客户端.它允许在不同的资源中(Reso ...

  2. C#管理异常和错误

    C#管理异常和错误 1.try/catch捕捉异常的语句块,其中try{}中是写可能会出错的程序代码,catch{}中是抛出异常的代码:一个try后可以有多个catch. 2.异常采用继承层次结构进行 ...

  3. C语言学习008:标准错误

    在上一节中的数据文件中(C语言学习007:重定向标准输入和输出),如果文件中的数据包含非法数据,如何让程序显示一条错误的提示消息呢?就需要用到标准错误 #include <stdio.h> ...

  4. 解决Linux c语言运行时候“段错误 (核心已转储)”问题-采用gdb 解决

    编译没有警告,没有错误,运行就打印 段错误 (核心已转储) 网上找了一下,都是各种问题,都推荐用gdb 调试解决,咱也来趁机学习gdb一下.   gcc+gdb)输入命令行 运行 sudo apt-g ...

  5. 使用Jena RDF API 开发脚本语言管理资源描述框架模型

    摘要 资源描述框架(Resource Description Framework RDF)是一种以XML格式描述元数据的标准格式.Jena是一种用于将关系数据库或是文本文件中所表示的数据建立为元数据模 ...

  6. Swift5 语言指南(十九) 错误处理

    错误处理是响应程序中的错误条件并从中恢复的过程.Swift为在运行时抛出,捕获,传播和操纵可恢复的错误提供了一流的支持. 某些操作无法保证始终完成执行或生成有用的输出.Optionals用于表示缺少值 ...

  7. [译]Unity3D内存管理——对象池(Object Pool)

    原文地址:C# Memory Management for Unity Developers (part 3 of 3), 其实从原文标题可以看出,这是一系列文章中的第三篇,前两篇讲解了从C#语言本身 ...

  8. (c语言编程)出现错误:null undeclared identifier

    原因:没有添加头文件#include <stdio.h> 添加完头文件后,错误消失

  9. [译]C#控制管理VisualSVN Server

    VisualSVN Server可以用WMI接口管理(Windows Management Instrumentation). VisualSVN Server安装的计算机中,位于%VISUALSVN ...

随机推荐

  1. 010.kubernets的调度系统之daemonset

    daemonset简单操作使用 Deployment 是 Kubernetes 中用于处理无状态服务的资源,而 StatefulSet 是用于支持有状态服务的资源,这两种不同的资源从状态的角度对服务进 ...

  2. 009.kubernets的调度系统之污点和容忍

    Taints和Tolerations(污点和容忍) Taint需要与Toleration配合使用,让pod避开那些不合适的node.在node上设置一个或多个Taint后,除非pod明确声明能够容忍这 ...

  3. 逗号字符的使用、字符数组与字符串数组、sizeof与strlen

    (1)连接两个表达式为一个表达式 for(ux=0,uxt=1;uxt<444;ux++,uxt++) 允许通过编译:他可以给FOR循环更多的初始化值: (2)一般定义的话要区别只有 字符数组 ...

  4. Java 的序列化 (Serializable)(Day_09)

    我们的火,要把世界都点燃 运行环境 JDK8 + IntelliJ IDEA 2018.3 什么是序列化,反序列化 序列化是将对象状态转换为可保持或传输的格式的过程. 与序列化相对的是反序列化,它将流 ...

  5. kotlin知识点

    主构造函数里的参数,如果不声明为var或者val,则这个参数一般是用来初始化父类.它不算是这个类的字段,它的作用域只在主构造函数当中. val 的对象不仅数据不能变, 引用也不能变. //自定义的类似 ...

  6. python 匹配中文字符

    参考: http://hi.baidu.com/nivrrex/blog/item/e6ccaf511d0926888d543071.html           http://topic.csdn. ...

  7. Element-ui Popconfirm气泡确认框的确认及取消事件不生效

    Element-ui 官方文档对 Popconfirm气泡确认框的一些属性及事件的描述不够详细,导致第一次使用时会遇到各种各样的问题 对确定事件及取消事件描述如下: 但是如果给组件绑定@confirm ...

  8. MyBatis-框架使用和分析

      一.基础知识 MyBatis 是一款优秀的持久层框架,它支持自定义 SQL.存储过程以及高级映射.MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作.MyBatis 可 ...

  9. CVPR2020论文介绍: 3D 目标检测高效算法

    CVPR2020论文介绍: 3D 目标检测高效算法 CVPR 2020: Structure Aware Single-Stage 3D Object Detection from Point Clo ...

  10. ADAS虚拟车道边界生成

    ADAS虚拟车道边界生成 Virtual Lane Boundary Generation for Human-Compatible Autonomous Driving: A Tight Coupl ...