// SetLimit limits the number of active goroutines in this group to at most n. // A negative value indicates no limit. // // Any subsequent call to the Go method will block until it can add an active // goroutine without exceeding the configured limit. // // The limit must not be modified while any goroutines in the group are active. func(g *Group) SetLimit(n int) { if n < 0 { g.sem = nil return } iflen(g.sem) != 0 { panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem))) } g.sem = make(chan token, n) }
funcTestTryGo(t *testing.T) { g := &errgroup.Group{} n := 42 g.SetLimit(42) ch := make(chanstruct{}) fn := func()error { ch <- struct{}{} returnnil } for i := 0; i < n; i++ { if !g.TryGo(fn) { t.Fatalf("TryGo should succeed but got fail at %d-th call.", i) } } if g.TryGo(fn) { t.Fatalf("TryGo is expected to fail but succeeded.") } gofunc() { for i := 0; i < n; i++ { <-ch } }() g.Wait()
if !g.TryGo(fn) { t.Fatalf("TryGo should success but got fail after all goroutines.") } gofunc() { <-ch }() g.Wait()
// Switch limit. g.SetLimit(1) if !g.TryGo(fn) { t.Fatalf("TryGo should success but got failed.") } if g.TryGo(fn) { t.Fatalf("TryGo should fail but succeeded.") } gofunc() { <-ch }() g.Wait()
// Block all calls. g.SetLimit(0) for i := 0; i < 1<<10; i++ { if g.TryGo(fn) { t.Fatalf("TryGo should fail but got succeded.") } } g.Wait() }
funcTestGoLimit(t *testing.T) { const limit = 10
g := &errgroup.Group{} g.SetLimit(limit) var active int32 for i := 0; i <= 1<<10; i++ { g.Go(func()error { n := atomic.AddInt32(&active, 1) if n > limit { return fmt.Errorf("saw %d active goroutines; want ≤ %d", n, limit) } time.Sleep(1 * time.Microsecond) // Give other goroutines a chance to increment active. atomic.AddInt32(&active, -1) returnnil }) } if err := g.Wait(); err != nil { t.Fatal(err) } }
TestTryGo 里面展示了各种情况下(包括调用 SetLimit 设置并发与否)应该得到的是 true or false