sync.WaitGroup 作用
- 等待一组协程完成
- 工作原理:通过计数器来获取协程的完成情况
- 启动一个协程时计数器+1,协程退出时计数器-1
- 通过wait方法阻塞主协程,等待计数器清零后才能继续执行后续操作
sync.WaitGroup 应用场景
- 通过协程并行执行一组任务,且任务全部完成后才能进行下一步操作的情况
- 例如: 汽车的生成,所有零件可以并行生产,只能等所有零件生成完成后,才能组装
sync.WaitGroup 陷阱
- 协程间传递时需要以指针的方式或闭包的方式引用 WaitGroup 对象。否则将会造成死锁
sync.Cond 作用
- 设置一组协程根据条件阻塞,可以根据不同的条件阻塞
- 可以根据条件唤醒相对应的协程
sync.Cond 应用场景
- 应用于一发多收的场景,即一组协程需要等待某一个协程完成一些前置准备的情况
sync.Cond 注意事项
- 被叫方必须持有锁
- 主叫方可以持有锁,但允许不持有
- 尽可能的减少无效唤醒
WaitGroup代码示例
代码1
循环50亿次和协程时间对比:
package waitgroup
import (
"fmt"
"sync"
"time"
)
func WaitGroupCase() {
var a, b = 1000, 10000
start := time.Now()
for i := 0; i < 5000000000; i++ {
multi(a, b)
}
t := time.Since(start)
fmt.Println(t)
fmt.Println("------------------")
start2 := time.Now()
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 500000000; j++ {
multi(a, b)
}
}()
}
wg.Wait()
t2 := time.Since(start2)
fmt.Println(t2)
}
func multi(a, b int) int {
return a * b
}
代码2
package waitgroup
import (
"fmt"
"sync"
"time"
)
func WaitGroupCase1() {
ch := make(chan []int, 1000)
start := time.Now()
wg2 := sync.WaitGroup{}
wg2.Add(1)
go func() {
defer wg2.Done()
i := 0
for itme := range ch {
fmt.Println(multi(itme[0], itme[1]))
i++
}
time.Sleep(3 * time.Second)
fmt.Println("数据处理完成,数据条数:", i)
}()
wg := &sync.WaitGroup{}
for i := 0; i < 2; i++ {
wg.Add(1)
wg2.Add(1)
go func(wg1 *sync.WaitGroup) {
defer wg1.Done()
defer wg2.Done()
for j := 0; j < 500; j++ {
ch <- []int{i, j}
}
}(wg)
}
wg.Wait()
close(ch)
wg2.Wait()
t := time.Since(start)
fmt.Println("处理时长为:", t)
}
func multi(a, b int) int {
return a * b
}
代码3:
求出800以内的素数并打印出来
package main
import (
"fmt"
"sync"
)
func putNum(intChan chan int, wg *sync.WaitGroup) {
defer wg.Done() // 协程结束时,计数-1
for i := 1; i <= 800; i++ {
intChan <- i
}
close(intChan)
}
func primeNum(intChan chan int, primeChan chan int, wg *sync.WaitGroup) {
defer wg.Done() // 协程结束时,计数-1
for {
num, ok := <-intChan
if !ok {
break
}
if num == 1 {
continue
}
flag := true
for i := 2; i <= num/2; i++ {
if num%i == 0 {
flag = false
break
}
}
if flag {
fmt.Println("primeNum写入了", num)
primeChan <- num
}
}
fmt.Println("有一个primeNum协程因为取不到数据,退出了")
}
func main() {
intChan := make(chan int, 100)
primeChan := make(chan int, 10)
wg := sync.WaitGroup{}
wg.Add(1) // 启动一个协程 计数+1
go putNum(intChan, &wg)
// 开启4个协程,从intChan取出数据,并判断是否是素数,
// 如果是素数,就放到primeChan中
for i := 0; i < 4; i++ {
wg.Add(1) // 启动一个协程 计数+1
go primeNum(intChan, primeChan, &wg)
}
go func() {
//等待所有协程结束后关闭primeChan
wg.Wait()
close(primeChan)
}()
for v := range primeChan {
fmt.Println(v)
}
fmt.Println("main()进程结束")
}