首页 文章资讯内容详情

Golang sync.WaitGroup

2026-06-01 4 花语

本文内容纲要:

-练习题 -李培冠博客

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