【Go】优雅上下线(二)
2022-04-19 本文已影响0人
如雨随行2020
@[toc]
关闭
上一篇已经讲了如何捕获Unix信号,这一篇我们来探究一下在多个协程工作的情况下如何正确关闭程序。一般在项目中不会仅一个协程处理任务,而是主协程做完初始化后,启动若干个子协程去处理各自的业务,甚至子协程继续开启子协程。
如此一来,在关闭程序就有两个问题需要解决:
- 子协程如何收到关闭信号
- 主协程如何知道所有子协程都完成了
主协程需要等待所有子协程都完成后才能退出,因为主协程退出后,子协程会被强制关闭。
准确来说,第二个是比较常见的问题,不仅限于今天要讲的优雅上下线
一、子协程关闭
在Go中,对一个已经关闭的Channel取值是可以的,所以子协程可以调用上一篇【Go】优雅上下线(一)监控协程中的Closed()
直接获取是否收到Unix信号或者调用GetStopChan()
拿到stop
通道自己监听。这两种方式基本能解决问题一
二、主协程等待所有子协程关闭
官方推荐使用sync.WaitGroup
import "fmt"
import "time"
import "sync"
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
time.Sleep(1 * time.Second)
fmt.Println(i)
}(i)
}
wg.Wait() // 等待
fmt.Println("main exit")
}
三、对子协程个数的限制
在解决优雅上下线这个问题时,突然看到我导师之前写过的一个工具类,这个工具类可以限制一个同步任务中并发的协程数,同时还可以保证Wait所有子协程完成
// GoLimit 限制一个同步任务中并发的协程数
type GoLimit struct {
size int
c chan struct{}
wg *sync.WaitGroup
}
func NewGoLimit(size int) *GoLimit {
return &GoLimit{
size: size,
c: make(chan struct{}, size),
wg: &sync.WaitGroup{},
}
}
func (g *GoLimit) Run(f func()) {
g.wg.Add(1)
g.c <- struct{}{}
go func() {
f()
g.wg.Done()
<-g.c
}()
}
func (g *GoLimit) Wait() {
g.wg.Wait()
}