js css html

【Go】优雅上下线(二)

2022-04-19  本文已影响0人  如雨随行2020

@[toc]

关闭

上一篇已经讲了如何捕获Unix信号,这一篇我们来探究一下在多个协程工作的情况下如何正确关闭程序。一般在项目中不会仅一个协程处理任务,而是主协程做完初始化后,启动若干个子协程去处理各自的业务,甚至子协程继续开启子协程。
如此一来,在关闭程序就有两个问题需要解决:

  1. 子协程如何收到关闭信号
  2. 主协程如何知道所有子协程都完成了
    主协程需要等待所有子协程都完成后才能退出,因为主协程退出后,子协程会被强制关闭。

准确来说,第二个是比较常见的问题,不仅限于今天要讲的优雅上下线

一、子协程关闭

在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()
}
上一篇下一篇

猜你喜欢

热点阅读