Go主协程如何等其余协程完再操作?

参考回答

在 Go 中,如果主协程需要等待其他协程执行完毕后再继续操作,可以通过以下几种方式实现:

  1. 使用 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 完成一组工作,可以通过调整 AddDone 的调用,精确同步主协程和子协程的执行。


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.Contextsync.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 是最常见、最推荐的方式,适用于绝大多数场景,而 channelcontext 提供了灵活性,可以根据具体需求选择合适的实现方式。

发表评论

后才能评论