巧妙使用WaitGroupt处理错误

1. 写在前面

使用Go的众多好处之一是它在并发方面十分简单,而大家比熟悉的WaitGroups就是一个很好的例子。虽然在并发处理上十分的方便,但要想有效地处理并发和错误可能很棘手。

本篇文章旨在概述如何在不停止程序执行的情况下,运行多个goroutine并有效处理任何错误。

2. 具体实现

对于这个如何上,可以简单的概括为以下三点:

  • 两个channel。这两个channel的作用是用于传递错误传递WaitGroup何时完成

  • 一个groutine。主要作用是用于监听WaitGroup是否完成,如果完成了,将会关闭某个channel。

  • 一个Select。它用于监听出现的错误或WaitGroup完成与否,无论谁先结束,那么Select就会先执行谁。

具体代码如下:

main.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 package main

 import (
     "errors"
     "fmt"
     "sync"
 )

 // ErrorHandler 返回一个错误。
 func ErrorHandler() error {
     return errors.New("generated errors")
 }

 func main() {
     // 创建两个channel,一个用于传递错误,另一个表示WaitGroup是否结束。
     errCh := make(chan error)
     wgCh := make(chan bool)

     var wg sync.WaitGroup

     wg.Add(2)

     go func() {
         fmt.Println("WaitGroup 1st.")

         // 这里可以定义我们需要执行的操作

         wg.Done()
     }()

     go func() {
         fmt.Println("WaitGroup 2nd")

         // 返回自定义的错误
         if err := ErrorHandler(); err != nil {
             errCh <- err
         }

         wg.Done()
     }()

     go func() {
         wg.Wait()
         close(wgCh)
     }()

     // 当有错误返回或WaitGroup执行结束时会被执行。
     select {
     case <-wgCh:
         break
     case err := <-errCh:
         close(errCh)
         panic(err)
     }

     fmt.Println("Main func ended!")
 }

运行上述代码,我们可以得到以下的输出,也从而能验证我们已经基于此达到了我们的目的。

output
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 PS C:\Users\jeffrey\Desktop\hello> go run main.go
 WaitGroup 1st.
 WaitGroup 2nd
 panic: generated errors.

 goroutine 1 [running]:
 main.main()
         C:/Users/jeffrey/Desktop/hello/main.go:53 +0x2a6
 exit status 2
 PS C:\Users\jeffrey\Desktop\hello>

欢迎关注我的微信公众号[double12gzh]:



您好! 我是大海星,专注云计算/云原生开发与架构设计。欢迎关注我的微信公众号[double12gzh]。