有时我们可能想既在外层循环中实现多协程并发,还想在内层循环中实现多协程并发,那么我们需要同时在内层和外层使用 WaitGroup()
来控制主协程不退出。
下面是一个 demo:
博客平台纯手敲,可能存在字符拼写错误
import ( "fmt" "sync" ) func handleTask() { demoList := []string{"123", "456", "abc", "ddd"} var wg = sync.WaitGroup{} for idx, item := range demoList { // 每个元素创建一个新协程去处理 wg.Add(1) // go func(idx int){ defer wg.Done() // 协程退出前将 wg 计数器减一,否则最后计数器无法减为0,会一直卡在 wg.wait() 那一行 // 内部还想新建子协程去做不同的事,减少执行时间 var innerWg = sync.WaitGroup{} // innerWg.Add(1) go func(){ defer innerWg.Done() // 子协程退出前将 innerWg 的计数器减一 fmt.Printf("子协程一正在执行") } innerWg.Add(1) go func(){ defer innerWg.Done() // 子协程退出前将 innerWg 的计数器减一 fmt.Printf("子协程二正在执行") } innerWg.Wait() // 等所有所有子协程执行完才继续往下执行 fmt.Printf("外层协程%d即将执行完毕", idx) }(idx) } wg.Wait() // 等待 wg 的计数器减为0后才继续往下执行,等待所有的协程处理完毕 fmt.Println("Execution completed. will exit") } func main() { handleTask() }
- sync.WaitGroup{}:一个内部加锁的计数器,Add(num) 函数会使计数器加上对应的 num 值;Done() 函数会使计数器减一;Wait() 函数会一直阻塞程序的继续向下运行,直到计数器减为0。
- Add() 函数需要在子协程开辟前执行;Done() 函数最好在子协程最开始的 defer 中执行,保证子协程退出后一定会将计数器减一,否则主协程可能因为计数器不为 0 一直卡主;Wait() 函数一般放在主协程里,且是子协程执行完毕后。
- 如果需要控制协程的数量,还得引入 channel ,参考:5、Go是否可以无限go? 如何限定数量?,文末是从此文拷贝过来的一段代码
下面是内层子协程并发的另一种写法
博客平台纯手敲,可能存在字符拼写错误
import ( "fmt" "sync" ) func handleTask() { demoList := []string{"123", "456", "abc", "ddd"} var wg = sync.WaitGroup{} for idx, item := range demoList { // 每个元素创建一个新协程去处理 wg.Add(1) // go func(idx int){ defer wg.Done() // 协程退出前将 wg 计数器减一,否则最后计数器无法减为0,会一直卡在 wg.wait() 那一行 // 内部还想新建子协程去做不同的事,减少执行时间 var innerWg = sync.WaitGroup{} // for i := 1; i < 3; i++ { innerWg.Add(1) go func(){ defer innerWg.Done() // 子协程退出前将 innerWg 的计数器减一 fmt.Printf("子协程 %d 正在执行\n", i) } } fmt.Printf("外层协程%d即将执行完毕", idx) }(idx) } wg.Wait() // 等待 wg 的计数器减为0后才继续往下执行,等待所有的协程处理完毕 fmt.Println("Execution completed. will exit") } func main() { handleTask() }
channel 与 sync 同步组合方式控制协程数量
package main import ( "fmt" "math" "sync" "runtime" ) var wg = sync.WaitGroup{} func busi(ch chan bool, i int) { fmt.Println("go func ", i, " goroutine count = ", runtime.NumGoroutine()) <-ch wg.Done() } func main() { //模拟用户需求go业务的数量 task_cnt := math.MaxInt64 ch := make(chan bool, 3) for i := 0; i < task_cnt; i++ { wg.Add(1) ch <- true go busi(ch, i) } wg.Wait() }