今天我们来谈谈Golang的同步等待组
我们现在开十条子协程,然后当十条子协程全部结束后,主协程立马结束。动动你的小脑袋,想一想应该怎么做?如果是一条子协程的话就很容易实现,当这条子协程结束时让主协程结束就行了。但是我们现在是10条,让任何一条子协程发布让主协程结束的命令都不行,因为你无法确定哪一条子协程是最后结束的。所以我们现在用上了等待组。
等待组是什么原理呢?创造一个子协程就登记一下,然后子协程干完活就将其除名,名单除干净了就结束主协程。
我们来看看等待组的有关示例:
funcmain(){fmt.Println(time.Now())varwgsync.WaitGroup//起一个协程就加一wg.Add(1)gofunc(){fori:=0;i<5;i++{fmt.Println(i)//相当于阻塞一秒,读到时间<-time.After(time.Second)}fmt.Println(time.Now())//活干完之后减一wg.Done()}()wg.Add(1)gofunc(){variintticker:=time.NewTicker(time.Second)for{<-ticker.Ci++fmt.Println("秒表",i)ifi>9{break}}fmt.Println(time.Now())wg.Done()}()//等待组阻塞等待至记录清零为止wg.Wait()fmt.Println("END")}
这段代码是建立一条协程就使用wg.Add(1)给等待组加一,然后活干完之后就减一。
WaitGroup 等待一组 goroutine 完成。主 goroutine 调用 Add 来添加要等待的 goroutine 的数量。 然后每个 goroutine 运行并在完成时调用 Done。 同时,Wait 可用于阻塞,直到所有 goroutine 完成。
Add()方法是用来设置等待组中的计数器的值,我们可以理解每个等待组中都有一个计数器,这个计数器可以用来表示这个等待组中要执行的协程数量。如果计数器为零,那么表示被阻塞的协程都被释放了。
Done()方法就是当同步等待组中的某个协程执行完毕后,使同步等待组中的计数器数量减一。
这里一条协程5秒结束,另一条协程10秒结束,那按理来说应该是10秒结束,我们来看看运行结果吧!
2021-08-2519:10:28.3511953+0800CSTm=+0.01698940101秒表12秒表2秒表33秒表44秒表52021-08-2519:10:33.4452142+0800CSTm=+5.111008301秒表6秒表7秒表8秒表9秒表102021-08-2519:10:38.4369656+0800CSTm=+10.102759701END
下面来谈谈几个需要注意的事项:
1.我们使用等待组时不可以在wg.Add()中填入负数,不然会导致报错。报错结果如下:
panic: sync: negative WaitGroup counter
这点需要注意。
2.WaitGroup对象不是一个引用类型
在通过函数传值时需要使用地址,需要通过指针传值,不然程序会出现死锁!
作者:ReganYue