在下面的例子中,我们在一个循环中用一个缓冲通道运行有限的goroutines,以完成许多工作。每个例子都有自己的要求,并使用不同的软件包,如图所示。
-
errgroup 每次最多运行10个goroutine,完成100个作业,一旦goroutine失败,就强制终止程序,或者在完成后正常存在。然而,如果程序运行时间超过1秒,则优雅地终止它,不需要等待goroutine完成。
-
waitgroup 每次最多运行10个goroutine,一直到100个作业结束。然而,如果程序运行时间超过1秒,则优雅地终止它,而不等待goroutines完成。
package main
import (
"context"
"fmt"
"log"
"time"
"golang.org/x/sync/errgroup"
)
var (
done = make(chan struct{})
workers = make(chan struct{}, 10)
)
func main() {
go func() {
eg, ctx := errgroup.WithContext(context.Background())
for i := 1; i <= 100; i++ {
i := i
workers <- struct{}{}
eg.Go(func() error {
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := do(ctx, i); err != nil {
return err
}
<-workers
return nil
}
})
}
if err := eg.Wait(); err != nil {
log.Fatalln("force exit", err)
}
done <- struct{}{}
}()
for {
select {
case <-time.After(time.Second):
log.Println("graceful exit at timeout")
return
case <-done:
log.Println("all done")
return
}
}
}
func do(ctx context.Context, i int) error {
time.Sleep(time.Millisecond * 100)
fmt.Println("JOB:", i)
return nil
}
waitgroup
package main
import (
"fmt"
"log"
"sync"
"time"
)
var (
done = make(chan struct{})
workers = make(chan struct{}, 10)
waiter = &sync.WaitGroup{}
)
func main() {
go func() {
for i := 1; i <= 100; i++ {
waiter.Add(1)
workers <- struct{}{}
go func(i int) {
defer waiter.Done()
do(i)
<-workers
}(i)
}
waiter.Wait()
done <- struct{}{}
}()
for {
select {
case <-time.After(time.Second):
log.Println("graceful exit at timeout")
return
case <-done:
log.Println("all done")
return
}
}
}
func do(i int) {
time.Sleep(time.Millisecond * 100)
fmt.Println("JOB:", i)
}