golang,go,博客,开源,编程
errgroup
和 WaitGroup
都是 Go 语言中用于并发编程的工具,但它们的功能和使用场景有所不同。我们可以通过对比这两个工具的特点来理解它们各自的优缺点以及应用场景。
WaitGroup
(等待组)sync.WaitGroup
是 Go 标准库中用于等待一组 Goroutine 执行完成的工具。它的核心功能是阻塞直到指定的所有 Goroutine 执行完毕。WaitGroup
不关心每个 Goroutine 执行的结果(即不关心错误),它仅仅是用来控制同步,确保所有 Goroutine 完成后再继续执行。
Add(delta int)
:增加或减少等待的 Goroutine 数量。Done()
:通知 WaitGroup
当前 Goroutine 已经完成。Wait()
:阻塞当前线程,直到所有 Goroutine 完成。package main
import (
"fmt"
"sync"
)
func doTask(id int, wg *sync.WaitGroup) {
defer wg.Done() // 完成时通知 WaitGroup
fmt.Printf("Task %d is done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // 添加一个待完成的任务
go doTask(i, &wg)
}
wg.Wait() // 等待所有任务完成
fmt.Println("All tasks are done")
}
缺点:WaitGroup
不能处理任务中的错误,它只关心任务是否完成,而不管任务中发生了什么。
errgroup
(错误组)errgroup
是 golang.org/x/sync/errgroup
包中的一个工具,它是基于 WaitGroup
的扩展,但加入了错误处理的能力。除了等待所有 Goroutine 执行完毕外,errgroup
还能捕获 Goroutine 中发生的错误,并确保如果任何 Goroutine 返回错误时,能及时停止其他任务。
Go(func() error)
:启动一个新的 Goroutine,并指定该 Goroutine 执行的任务及返回的错误。Wait()
:等待所有 Goroutine 完成,如果有任何一个 Goroutine 返回错误,Wait()
会立即返回第一个错误,并停止其他任务。package main
import (
"fmt"
"golang.org/x/sync/errgroup"
"net/http"
)
func fetchURL(url string) error {
// 模拟网络请求
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("failed to fetch %s: %w", url, err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("non-200 response for %s: %d", url, resp.StatusCode)
}
fmt.Printf("Successfully fetched %s\n", url)
return nil
}
func main() {
var g errgroup.Group
urls := []string{
"https://example.com",
"https://google.com",
"https://nonexistenturl.com", // 这个会失败
}
// 启动多个 Goroutine 来抓取网页
for _, url := range urls {
url := url // 捕获局部变量
g.Go(func() error {
return fetchURL(url)
})
}
// 等待所有任务完成,如果遇到错误,返回第一个错误
if err := g.Wait(); err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Println("All URLs fetched successfully!")
}
}
优点:
errgroup
会自动收集和返回 Goroutine 执行中的第一个错误。errgroup
会停止剩余的任务,并返回错误。errgroup
与 WaitGroup
的对比特性 | WaitGroup | errgroup |
---|---|---|
错误处理 | 不处理错误,无法集中管理错误。 | 集中处理错误,返回第一个遇到的错误。 |
任务停止 | 无法在任务出错时停止其他 Goroutine。 | 可以在某个任务出错时停止其他 Goroutine。 |
适用场景 | 适合只关心所有 Goroutine 是否完成的场景。 | 适合关心 Goroutine 错误的场景,尤其是当出错时需要立即停止其他任务。 |
返回值 | 无法返回错误,只能等待任务完成。 | 返回任务执行中的第一个错误。 |
API 简洁度 | API 简单,只需要Add 、Done 、Wait 。 | API 比较灵活,增加了Go(func() error) 和错误处理机制。 |
errgroup
?WaitGroup
?WaitGroup
适合用在没有错误处理需求的场景,它只关心任务是否完成。errgroup
更适用于需要处理多个并发任务,并且需要集中管理错误的场景。它让你在并发任务中遇到第一个错误时能够及时停止其他任务,并返回错误。