Go 学习笔记 - 并发编程 ☞ errgroup

本文最后更新于:2023年4月26日 晚上

描述

Go 的优势无非在于并发编程,当你需要对数组中每一个元素都组装一个信息的时候,就涉及到并发处理的问题,毕竟根据组装数据的复杂程度,处理时间会指数型上涨,下面通过 sync.WaitGroup 和 errgroup.Group 两中方式分别对这种情况进行了处理。

sync.WaitGroup demo

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
58
59
60
61
62
63
64
package main

import (
"context"
"encoding/json"
"sync"
"testing"

"github.com/stretchr/testify/assert"
)

type Class struct {
Id int64 `json:"id"`
Name string `json:"name"`
Grade string `json:"grade"`
Student []*Student `json:"student"`
}

type Student struct {
Id int64 `json:"id"`
ClassId int64 `json:"classId"`
Name string `json:"name"`
}

func TestWaitGroup(t *testing.T) {
var (
mu sync.Mutex
wg = sync.WaitGroup{}
errs = make([]error, 0)
classArr = []*Class{{Id: 1}, {Id: 2}, {Id: 3}}
)

wg.Add(len(classArr))
for _, item := range classArr {
go func(class *Class) {
defer wg.Done()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
students, err := queryAllStudent(ctx, class.Id)
if err != nil {
mu.Lock()
errs = append(errs, err)
mu.Unlock()
return
}
class.Student = students
}(item)
}
wg.Wait()
assert.Equal(t, len(errs), 0)

bytes, err := json.Marshal(classArr)
assert.Nil(t, err)

t.Log(string(bytes))
}

func queryAllStudent(ctx context.Context, classId int64) ([]*Student, error) {
return map[int64][]*Student{
1: []*Student{{Id: 1}, {Id: 2}, {Id: 3}},
2: []*Student{{Id: 4}, {Id: 5}, {Id: 6}},
3: []*Student{{Id: 7}, {Id: 8}, {Id: 9}},
}[classId], nil
}

我之前都是这么写的,但是这样就会有一个问题,就是 err 的捕获比较难搞,要声明一个 errs ,且返回 err 的时候取 errs 的哪一个元素都感觉不是很合理。

errgroup.Group demo

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
58
59
60
61
package main

import (
"context"
"encoding/json"
"testing"

"golang.org/x/sync/errgroup"
"github.com/stretchr/testify/assert"
)

type Class struct {
Id int64 `json:"id"`
Name string `json:"name"`
Grade string `json:"grade"`
Student []*Student `json:"student"`
}

type Student struct {
Id int64 `json:"id"`
ClassId int64 `json:"classId"`
Name string `json:"name"`
}

func TestErrGroup(t *testing.T) {
var (
eg = new(errgroup.Group)
classArr = []*Class{{Id: 1}, {Id: 2}, {Id: 3}}
)

for _, item := range classArr {
func(class *Class) {
eg.Go(func() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
students, err := queryAllStudent(ctx, class.Id)
if err != nil {
return err
}
class.Student = students
return nil
})
}(item)
}
if err := eg.Wait(); err != nil {
assert.Nil(t, err)
}

bytes, err := json.Marshal(classArr)
assert.Nil(t, err)

t.Log(string(bytes))
}

func queryAllStudent(ctx context.Context, classId int64) ([]*Student, error) {
return map[int64][]*Student{
1: []*Student{{Id: 1}, {Id: 2}, {Id: 3}},
2: []*Student{{Id: 4}, {Id: 5}, {Id: 6}},
3: []*Student{{Id: 7}, {Id: 8}, {Id: 9}},
}[classId], nil
}

这样 err 的处理看起来舒服多了,只是 errgroup 目前不支持传参,导致不得不在外面加一层闭包,但是如果需要 err 捕获的话,用起来确实是比 waitGroup 舒服些。


Go 学习笔记 - 并发编程 ☞ errgroup
https://agopher.com/2023/04/26/tech/2023_go_err_group/
作者
冷宇生(Allen)
发布于
2023年4月26日
更新于
2023年4月26日
许可协议