您的位置:首页技术文章

Golang中的sync包的WaitGroup操作

【字号: 日期:2023-10-28 16:34:33浏览:5作者:馨心

sync的waitgroup功能

WaitGroup

使用多线程时,进行等待多线程执行完毕后,才可以结束函数,有两个选择

channel

waitgroup

首先使用channel

func add (n *int , isok chan bool){for i :=0 ;i <1000 ; i ++ {*n = *n + 1}isok <- true}func main () {var ok = make(chan bool , 2)var i,u = 0,0go add(&i , ok)go add(&i , ok)for <- ok {u++if u == 2 {break}}fmt.Println(i)}

但是,如果线程一旦增多,就会导致代码冗余,增加负担,可以使用sync.WaitGroup包

func add (n *int , wait *sync.WaitGroup) { for i := 0 ; i <1000 ; i ++ { *n = *n + 1 } defer wait.Done()}func main () { var wait sync.WaitGroup var i = 0 wait.Add(2) go add(&i , &wait) go add(&i , &wait) wait.Wait() fmt.Println(i) }仿sync.WaitGroup功能

type Wait struct {//创建一个结构体 Num int//线程的个数 ok chan bool//线程与函数通信的管道}func (t *Wait) Add (n int){//初始化线程个数 t.Num = n t.ok = make(chan bool , n)}func (t *Wait) Done (){//执行完一个线程之后,执行的函数,每执行完一个线程,调用函数,使用通信管道进行传输一个true t.ok <- true}func (t *Wait) Wait () {//等待函数,每次管道中放入一个true,说明,执行完一个线程,t.Num--,如果等于0说明所有线程执行结束 for <- t.ok { t.Num-- if t.Num == 0 { break } }}

补充:Golang的WaitGroup陷阱

sync.WaitGroup是并发环境中,一个相当常用的数据结构,用来等待所有协程的结束,在写代码的时候都是按着例子的样子写的,也没用深究过它的使用。前几日想着能不能在协程中执行Add()函数,答案是不能,这里介绍下。

陷阱在WaitGroup的3个函数的调用顺序上。先回顾下3个函数的功能:

Add(delta int):给计数器增加delta,比如启动1个协程就增加1。

Done():协程退出前执行,把计数器减1。

Wait():阻塞等待计数器为0。

考一考

下面的程序是创建了协程father,然后father协程创建了10个子协程,main函数等待所有协程结束后退出,看看下面代码有没有什么问题?

package main import ( 'fmt' 'sync') func father(wg *sync.WaitGroup) { wg.Add(1) defer wg.Done() fmt.Printf('fathern') for i := 0; i < 10; i++ { go child(wg, i) }} func child(wg *sync.WaitGroup, id int) { wg.Add(1) defer wg.Done() fmt.Printf('child [%d]n', id)} func main() { var wg sync.WaitGroup go father(&wg) wg.Wait() log.Printf('main: father and all chindren exit')}

发现问题了吗?如果没有看下面的运行结果:main函数在子协程结束前就开始结束了。

fathermain: father and all chindren exitchild [9]child [0]child [4]child [7]child [8]陷阱分析

产生以上问题的原因在于,创建协程后在协程内才执行Add()函数,而此时Wait()函数可能已经在执行,甚至Wait()函数在所有Add()执行前就执行了,Wait()执行时立马就满足了WaitGroup的计数器为0,Wait结束,主程序退出,导致所有子协程还没完全退出,main函数就结束了。

正确的做法

Add函数一定要在Wait函数执行前执行,这在Add函数的文档中就提示了: Note that calls with a positive delta that occur when the counter is zero must happen before a Wait.。

如何确保Add函数一定在Wait函数前执行呢?在协程情况下,我们不能预知协程中代码执行的时间是否早于Wait函数的执行时间,但是,我们可以在分配协程前就执行Add函数,然后再执行Wait函数,以此确保。

下面是修改后的程序,以及输出结果。

package main import ( 'fmt' 'sync') func father(wg *sync.WaitGroup) { defer wg.Done() fmt.Printf('fathern') for i := 0; i < 10; i++ { wg.Add(1) go child(wg, i) }} func child(wg *sync.WaitGroup, id int) { defer wg.Done() fmt.Printf('child [%d]n', id)} func main() { var wg sync.WaitGroup wg.Add(1) go father(&wg) wg.Wait() fmt.Println('main: father and all chindren exit')}

fatherchild [9]child [7]child [8]child [1]child [4]child [5]child [2]child [6]child [0]child [3]main: father and all chindren exit

以上为个人经验,希望能给大家一个参考,也希望大家多多支持优爱好网。如有错误或未考虑完全的地方,望不吝赐教。

标签: Golang
相关文章: