概述
在年度调查中,关于开发人员在使用 Go 时面临的最大挑战,Go 中的错误管理总是容易引起争论,并且是一个反复出现的话题。然而,当涉及到在并发环境中处理错误或为同一个 goroutine
合并多个错误时,Go 提供了很棒的包,使管理多个错误变得容易。让我们看看如何合并由单个 goroutine
生成的多个错误。
单个 goroutine, 多个错误
例如,当您处理具有重试策略的代码时,将多个错误合并为一个错误会非常有用。这是我们需要收集生成的错误的基本示例:
这个程序读取和解析一个 CSV 文本,并显示发现的错误。将错误分组以获得完整的报告可能更方便。要将错误合并为一个,我们可以在两个很棒的包中进行选择:
- 使用 HashiCorp 的 go-multierror
输出结果如下:
这里的实现类似,下面是输出:
错误通过分号连接起来,没有任何其他格式。
对于每个包的性能,下面是一个基准测试结论:1
2
3name 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 的实现稍微慢一些,消耗更多的内存。但是,这个包的设计目的是将收集到的错误分组在一起,而不是每次都附加它们。当对错误进行分组时,结果很接近,但是代码不够优雅,因为它需要额外的步骤。以下是最新的测试结果:1
2
3name 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
接口,并在其自定义实现中实现了 Error() string
函数。
单个错误, 多个 goroutines
当处理多个 goroutines
来执行一个任务时,有必要正确管理结果和将错误聚合,以确保程序的正确性。
让我们从一个使用多个 goroutines
执行一系列操作的程序开始; 每个操作持续一秒钟:
为了说明错误传播,第三个 goroutine
的第一个操作将失败。事情是这样的:
正如预期的那样,这个程序大约需要 3 秒,因为大多数 goroutines
需要经历三个动作,每个动作需要 1 秒:1
go run . 0.30s user 0.19s system 14% cpu 3.274 total
但是,我们可能希望使 goroutines
相互依赖,并在其中一个失败时取消它们。避免不必要工作的解决方案是添加上下文,一旦 goroutine
失败,它就会取消它:
这正是 errgroup 所提供的;处理一组 goroutines
时的错误和上下文传播。下面是使用包 errgroup 的新代码:
程序现在运行得更快,因为它通过错误传播取消的上下文:1
go run . 0.30s user 0.19s system 38% cpu 1.269 total
该包提供的另一个好处是,我们不需要再担心等待组添加和标记 goroutines
完成。包为我们管理这些,我们只需要说我们准备好等待过程的结束。
译自:https://medium.com/a-journey-with-go/go-multiple-errors-management-a67477628cf1