Go主协程如何等其余协程完再操作?
参考回答
在 Go 中,如果主协程需要等待其他协程执行完毕后再继续操作,可以通过以下几种方式实现:
- 使用
sync.WaitGroup
sync.WaitGroup是 Go 提供的一个用于计数器的同步机制,可以通过它等待一组协程完成。示例:
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup // 启动 3 个协程 for i := 1; i <= 3; i++ { wg.Add(1) // 增加计数器 go func(id int) { defer wg.Done() // 完成后减少计数器 fmt.Printf("Goroutine %d finished\n", id) }(i) } wg.Wait() // 等待所有协程完成 fmt.Println("All Goroutines finished") }输出(顺序可能不同):
Goroutine 1 finished Goroutine 3 finished Goroutine 2 finished All Goroutines finished
详细讲解与拓展
1. sync.WaitGroup 的工作原理
Add(n int):增加计数器n。Done():减少计数器1。Wait():阻塞调用的协程,直到计数器减为0。示例:如果你需要控制多个 Goroutine 完成一组工作,可以通过调整
Add和Done的调用,精确同步主协程和子协程的执行。
2. 其他实现方式
方式 1:使用 channel
通过使用无缓冲 channel 和计数的方式,手动实现 Goroutine 的等待机制。
示例:
“`go
package main
import (
"fmt"
)
func main() {
ch := make(chan bool, 3) // 缓冲区大小等于协程数
<pre><code> for i := 1; i <= 3; i++ {
go func(id int) {
fmt.Printf("Goroutine %d finished\n", id)
ch <- true // 向 channel 发送完成信号
}(i)
}
// 等待所有协程完成
for i := 1; i <= 3; i++ {
<-ch // 接收完成信号
}
fmt.Println("All Goroutines finished")
</code></pre>
}
“`
输出(顺序可能不同):
“`
Goroutine 1 finished
Goroutine 3 finished
Goroutine 2 finished
All Goroutines finished
“`
方式 2:使用匿名函数阻塞
在简单场景下,主协程可以直接阻塞等待一个 channel。
示例:
“`go
package main
import (
"fmt"
)
func main() {
done := make(chan bool)
<pre><code> go func() {
fmt.Println("Goroutine finished")
done <- true // 通知主协程
}()
<-done // 等待 Goroutine 完成
fmt.Println("All Goroutines finished")
</code></pre>
}
“`
输出:
“`
Goroutine finished
All Goroutines finished
“`
方式 3:使用 context 进行协程管理
通过 context.Context 和 sync.WaitGroup 结合,管理多个协程,并在需要时取消未完成的任务。
示例:
“`go
package main
import (
"context"
"fmt"
"sync"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
<pre><code> var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
select {
case <-time.After(time.Duration(id) * time.Second):
fmt.Printf("Goroutine %d finished\n", id)
case <-ctx.Done():
fmt.Printf("Goroutine %d canceled\n", id)
}
}(i)
}
wg.Wait()
fmt.Println("All Goroutines finished or canceled")
</code></pre>
}
“`
输出:
“`
Goroutine 1 finished
Goroutine 2 finished
Goroutine 3 canceled
All Goroutines finished or canceled
“`
总结
主协程等待其余协程的常用方法包括:
1. sync.WaitGroup(推荐):高效且适合多 Goroutine。
2. 使用 channel:简单场景可以直接使用 channel 传递信号。
3. 使用 context:在需要控制超时或取消时非常有用。
sync.WaitGroup 是最常见、最推荐的方式,适用于绝大多数场景,而 channel 和 context 提供了灵活性,可以根据具体需求选择合适的实现方式。