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