优雅关闭协程

最后更新于:2022-04-02 02:36:37

[TOC] ## 概述 - 不管是使用 **sync.WaitGroup**,还是 **context.Background** 都可以防止协程泄露 - 都是通过 chan 的关闭来触发退出 ## 实例 ### sync.WaitGroup 控制一个结构体退出
main.go ``` package main import ( "fmt" "os" "os/signal" "sync" "time" ) type server struct { quit chan struct{} isrun bool wait sync.WaitGroup } func newServer() *server { return &server{ quit:make(chan struct{}), wait: sync.WaitGroup{}, isrun:true, } } func (s *server) close(){ close(s.quit) s.wait.Wait() } func (s *server) task(){ s.wait.Add(1) defer s.wait.Done() for { select { case <-s.quit: // 退出协程 fmt.Printf("%+v\n", "stop") return default: } fmt.Printf("%+v\n", "task") go s.subtask() time.Sleep(1*time.Second) } } func (s *server) subtask() { s.wait.Add(1) defer s.wait.Done() for { select { case <-s.quit: // 退出协程 fmt.Printf("%+v\n", "sub——stop") return default: } fmt.Printf("%+v\n", "subtask") time.Sleep(1*time.Second) } } func main() { s := newServer() defer s.close() go s.task() sig:=make(chan os.Signal) signal.Notify(sig,os.Interrupt) <-sig } ```

输出 ``` subtask subtask subtask subtask sub——stop sub——stop stop sub——stop sub——stop sub——stop ``` ### sync.WaitGroup 控制两个结构体退出 第一个结构体也可以采用 chan 的方式去控制退出
main.go ``` package main import ( "fmt" "os" "os/signal" "sync" "time" ) type Big struct { wait sync.WaitGroup } func (b *Big) run() { for i := 0; i < 10; i++ { b.wait.Add(1) go func(index int) { defer b.wait.Done() s := small{} s.Run(index) }(i) } } func (b *Big) Close() { fmt.Println("ready quit ..") b.wait.Wait() fmt.Println("quit ...") } type small struct { } func (s *small) Run(j int) { for i := 0; i < 5; i++ { fmt.Printf("%+v-%v\n", j, i) time.Sleep(1 * time.Second) } } func main() { var b Big b.run() defer b.Close() s := make(chan os.Signal) signal.Notify(s, os.Kill, os.Interrupt) <-s } ```

### context 控制退出 实例可参考 https://github.com/9b9387/zero
main.go ``` package main import ( "context" "fmt" "os" "os/signal" "time" ) type Big struct { quitBig chan struct{} } func NewBig() *Big{ return &Big{ quitBig: make(chan struct{}), } } func (b *Big)Run(){ ctx := context.Background() ctx,cancal:=context.WithCancel(ctx) defer cancal() for i:=0; i<5; i++ { go b.handle(ctx,i) } for { select { case <-b.quitBig: // 通过触发chan来触发 cancal() return } } } func (b *Big) handle(ctx context.Context,i int) { sm:=NewSmall() go sm.Write(ctx,i) go sm.Read(ctx) select { case <-sm.quitSmall: return } } func (b *Big)Close(){ close(b.quitBig) } //========================== // 子程序 //========================== type Small struct { quitSmall chan struct{} msg chan string } func NewSmall()*Small{ return &Small{ quitSmall: make(chan struct{}), msg: make(chan string,10), } } func (s *Small) Write(ctx context.Context,o int){ defer s.Close() for i:=0; i<5; i++ { // 每次执行判断ctx 是否断掉 select { case <-ctx.Done(): return default: } s.msg <- fmt.Sprintf("%v-%v", i, o) time.Sleep(1 * time.Second) } } func (s *Small) Read(ctx context.Context){ for{ select { case <-ctx.Done(): return case msg:=<-s.msg: fmt.Printf("%+v\n", msg) case <-s.quitSmall: return } } } func (s *Small) Close() { close(s.quitSmall) } func main() { big := NewBig() go big.Run() defer big.Close() s:=make(chan os.Signal,1) signal.Notify(s,os.Kill,os.Interrupt) <-s } ```

';