funcTestWaitGroup(t *testing.T) { wg := sync.WaitGroup{} var goroutineSize = 10 wg.Add(goroutineSize) for i := 0; i < goroutineSize; i++ { gofunc() { wg.Done() }() } wg.Wait() }
使用errorgoup.Wait
errorgroup.Group
A Group is a collection of goroutines working on subtasks that are
part of the same overall task.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
funcTestErrorGroup(t *testing.T) { var eg errgroup.Group var errorIndex = 8 for i := 0; i < GoroutineSize; i++ { // 这里特别容易出错,因为 eg.Go() 是在内部调用 go func() 产生了一个闭包 localVal := i eg.Go(func()error { // 在特定的位置抛出异常 if localVal == errorIndex { return errors.New(strconv.Itoa(localVal)) } returnnil }) }
err := eg.Wait()
assert.Error(t, err, strconv.Itoa(errorIndex)) }
一些使用技巧
使用channel的range和close操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
funcTestRangeAndClose(t *testing.T) { ch := make(chanint) var sum int gofunc() { for val := 0; val < GoroutineSize; val++ { sum += val ch <- val } close(ch) }()
// Cond implements a condition variable, a rendezvous point // for goroutines waiting for or announcing the occurrence // of an event. // // Each Cond has an associated Locker L (often a *Mutex or *RWMutex), // which must be held when changing the condition and // when calling the Wait method. // // A Cond must not be copied after first use. type Cond struct { noCopy noCopy
// L is held while observing or changing the condition L Locker
go consumer("consumer-0") go consumer("consumer-1") go consumer("consumer-2") go consumer("consumer-3") go consumer("consumer-4") go consumer("consumer-5") go consumer("consumer-6") go consumer("consumer-7") go consumer("consumer-8") go consumer("consumer-9")
go consumer("consumer-0") go consumer("consumer-1") go consumer("consumer-2") go consumer("consumer-3") go consumer("consumer-4") go consumer("consumer-5") go consumer("consumer-6") go consumer("consumer-7") go consumer("consumer-8") go consumer("consumer-9")
time.Sleep(1 * time.Minute) }
使用sync.Pool管理对象池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
funcTestPool(t *testing.T) { type User struct{ Id int } var autoIncrementId = 0 pool := sync.Pool{New: func() any { autoIncrementId++ return User{autoIncrementId} }}
for i := 0; i < LoopSize; i++ { u := pool.Get().(User) pool.Put(u) // user 应该一直被复用 assert.Equal(t, autoIncrementId, u.Id) } }