Golang sync.WaitGroup
本文内容纲要:
-练习题
-李培冠博客
Go语言中除了可以使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可以使用等待组进行多个任务的同步,等待组可以保证在并发环境中完成指定数量的任务
在sync.WaitGroup(等待组)类型中,每个sync.WaitGroup值在内部维护着一个计数,此计数的初始默认值为零。
等待组有下面几个方法可用,如下所示。
- func(wg*WaitGroup)Add(deltaint):等待组的计数器+1
- func(wg*WaitGroup)Done():等待组的计数器-1
- func(wg*WaitGroup)Wait():当等待组计数器不等于0时阻塞直到变0。
对于一个可寻址的sync.WaitGroup值wg:
-
我们可以使用方法调用wg.Add(delta)来改变值wg维护的计数。
-
方法调用wg.Done()和wg.Add(-1)是完全等价的。
-
如果一个wg.Add(delta)或者wg.Done()调用将wg维护的计数更改成一个负数,将会产生panic异常。
-
当一个协程调用了wg.Wait()时,
- 如果此时wg维护的计数为零,则此wg.Wait()此操作为一个空操作(noop);
- 否则(计数为一个正整数),此协程将进入阻塞状态。当以后其它某个协程将此计数更改至0时(一般通过调用wg.Done()),此协程将重新进入运行状态(即wg.Wait()将返回)。
等待组内部拥有一个计数器,计数器的值可以通过方法调用实现计数器的增加和减少。当我们添加了N个并发任务进行工作时,就将等待组的计数器值增加N。每个任务完成时,这个值减1。同时,在另外一个goroutine中等待这个等待组的计数器值为0时,表示所有任务已经完成。
什么意思?我们先来回忆一下之前我们为了保证子go程运行完毕,主go程是怎么做的:
packagemain
import(
"fmt"
"time"
)
funcmain(){
gofunc(){
fmt.Println("Goroutine1")
}()
gofunc(){
fmt.Println("Goroutine2")
}()
time.Sleep(time.Second)//睡眠1秒,等待上面两个子go程结束
}
我们为了让子go程可以顺序的执行完,在主go程中加入了等待。我们知道,这不是一个很好的解决方案,可以用channel来实现同步:
packagemain
import(
"fmt"
)
funcmain(){
ch:=make(chanstruct{})
count:=2//count表示活动的go程个数
gofunc(){
fmt.Println("Goroutine1")
ch<-struct{}{}//go程结束,发出信号
}()
gofunc(){
fmt.Println("Goroutine2")
ch<-struct{}{}//go程结束,发出信号
}()
forrangech{
//每次从ch中接收数据,表明一个活动的go程结束
count--
//当所有活动的go程都结束时,关闭channel
ifcount==0{
close(ch)
}
}
}
上面的解决方案是虽然已经比较好了,但是Go提供了更简单的方法:使用sync.WaitGroup
。
packagemain
import(
"fmt"
"sync"
)
funcmain(){
varwgsync.WaitGroup
wg.Add(2)//因为有两个动作,所以增加2个计数
gofunc(){
fmt.Println("Goroutine1")
wg.Done()//操作完成,减少一个计数
}()
gofunc(){
fmt.Println("Goroutine2")
wg.Done()//操作完成,减少一个计数
}()
wg.Wait()//等待,直到计数为0
}
可见用sync.WaitGroup
是最简单的方式。
强调一下:
- 计数器不能为负值:不能使用
Add()
或者Done()
给wg设置一个负值,否则代码将会报错。 - WaitGroup对象不是一个引用类型:在通过函数传值的时候需要使用地址。
官方文档看这里:https://golang.org/pkg/sync/#WaitGroup
练习题
1、写代码实现两个goroutine,其中一个产生随机数并写入到gochannel中,另外一个从channel中读取数字并打印到标准输出。最终输出五个随机数。
packagemain
import(
"fmt"
"math/rand"
"sync"
)
funcmain(){
ch:=make(chanint)
varwgsync.WaitGroup
wg.Add(1)
gofunc(){
deferwg.Done()
fori:=0;i<5;i++{
ch<-rand.Int()
}
close(ch)
}()
wg.Add(1)
gofunc(){
deferwg.Done()
fornum:=rangech{
fmt.Println("num=",num)
}
}()
wg.Wait()
}
李培冠博客
欢迎访问我的个人网站:
李培冠博客:lpgit.com
本文内容总结:练习题,李培冠博客,
原文链接:https://www.cnblogs.com/lpgit/p/13430871.html