Go语言中除了可使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可使用等待组进行多个任务的同步,等待组能够保证在并发环境中完成指定数量的任务git
在 sync.WaitGroup(等待组)类型中,每一个 sync.WaitGroup 值在内部维护着一个计数,此计数的初始默认值为零。golang
等待组有下面几个方法可用,以下所示。并发
对于一个可寻址的 sync.WaitGroup 值 wg:函数
等待组内部拥有一个计数器,计数器的值能够经过方法调用实现计数器的增长和减小。当咱们添加了 N 个并发任务进行工做时,就将等待组的计数器值增长 N。每一个任务完成时,这个值减 1。同时,在另一个 goroutine 中等待这个等待组的计数器值为 0 时,表示全部任务已经完成。oop
什么意思?咱们先来回忆一下以前咱们为了保证子 go 程运行完毕,主 go 程是怎么作的:网站
package main import ( "fmt" "time" ) func main() { go func() { fmt.Println("Goroutine 1") }() go func() { fmt.Println("Goroutine 2") }() time.Sleep(time.Second) // 睡眠 1 秒,等待上面两个子 go 程结束 }
咱们为了让子 go 程能够顺序的执行完,在主 go 程中加入了等待。咱们知道,这不是一个很好的解决方案,能够用 channel 来实现同步:code
package main import ( "fmt" ) func main() { ch := make(chan struct{}) count := 2 // count 表示活动的 go 程个数 go func() { fmt.Println("Goroutine 1") ch <- struct{}{} // go 程结束,发出信号 }() go func() { fmt.Println("Goroutine 2") ch <- struct{}{} // go 程结束,发出信号 }() for range ch { // 每次从 ch 中接收数据,代表一个活动的 go 程结束 count-- // 当全部活动的 go 程都结束时,关闭 channel if count == 0 { close(ch) } } }
上面的解决方案是虽然已经比较好了,可是 Go 提供了更简单的方法:使用 sync.WaitGroup
。协程
package main import ( "fmt" "sync" ) func main() { var wg sync.WaitGroup wg.Add(2) // 由于有两个动做,因此增长 2 个计数 go func() { fmt.Println("Goroutine 1") wg.Done() // 操做完成,减小一个计数 }() go func() { fmt.Println("Goroutine 2") wg.Done() // 操做完成,减小一个计数 }() wg.Wait() // 等待,直到计数为0 }
可见用 sync.WaitGroup
是最简单的方式。对象
强调一下:文档
Add()
或者 Done()
给 wg 设置一个负值,不然代码将会报错。官方文档看这里:https://golang.org/pkg/sync/#WaitGroup
一、写代码实现两个 goroutine,其中一个产生随机数并写入到 go channel 中,另一个从 channel 中读取数字并打印到标准输出。最终输出五个随机数。
package main import ( "fmt" "math/rand" "sync" ) func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() for i := 0; i < 5; i++ { ch <- rand.Int() } close(ch) }() wg.Add(1) go func() { defer wg.Done() for num := range ch { fmt.Println("num = ", num) } }() wg.Wait() }
欢迎访问个人我的网站:
李培冠博客:lpgit.com