问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何优雅地等待所有的goroutine退出

发布网友 发布时间:2022-04-24 01:14

我来回答

1个回答

热心网友 时间:2023-10-17 17:33

goroutine和channel是Go语言非常棒的特色,它们提供了一种非常轻便易用的并发能力。但是当您的应用进程中有很多goroutine的时候,如何在主流程中等待所有的goroutine 退出呢?
1 通过Channel传递退出信号
Go的一大设计哲学就是:通过Channel共享数据,而不是通过共享内存共享数据。主流程可以通过channel向任何goroutine发送停止信号,就像下面这样:
func run(done chan int) {
for {
select {
case <-done:
fmt.Println("exiting...")
done <- 1
break
default:
}

time.Sleep(time.Second * 1)
fmt.Println("do something")
}
}

func main() {
c := make(chan int)

go run(c)

fmt.Println("wait")
time.Sleep(time.Second * 5)

c <- 1
<-c

fmt.Println("main exited")
}

  
这种方式可以实现优雅地停止goroutine,但是当goroutine特别多的时候,这种方式不管在代码美观上还是管理上都显得笨拙不堪。
2 使用waitgroup
sync包中的Waitgroup结构,是Go语言为我们提供的多个goroutine之间同步的好刀。下面是官方文档对它的描述:
A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for.
Then each of the goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished.

通常情况下,我们像下面这样使用waitgroup:
创建一个Waitgroup的实例,假设此处我们叫它wg
在每个goroutine启动的时候,调用wg.Add(1),这个操作可以在goroutine启动之前调用,也可以在goroutine里面调用。当然,也可以在创建n个goroutine前调用wg.Add(n)
当每个goroutine完成任务后,调用wg.Done()
在等待所有goroutine的地方调用wg.Wait(),它在所有执行了wg.Add(1)的goroutine都调用完wg.Done()前阻塞,当所有goroutine都调用完wg.Done()之后它会返回。
那么,如果我们的goroutine是一匹不知疲倦的牛,一直孜孜不倦地工作的话,如何在主流程中告知并等待它退出呢?像下面这样做:
type Service struct {
// Other things

ch chan bool
waitGroup *sync.WaitGroup
}

func NewService() *Service {
s := &Service{
// Init Other things
ch: make(chan bool),
waitGroup: &sync.WaitGroup{},
}

return s
}

func (s *Service) Stop() {
close(s.ch)
s.waitGroup.Wait()
}

func (s *Service) Serve() {
s.waitGroup.Add(1)
defer s.waitGroup.Done()

for {
select {
case <-s.ch:
fmt.Println("stopping...")
return
default:
}
s.waitGroup.Add(1)
go s.anotherServer()
}
}
func (s *Service) anotherServer() {
defer s.waitGroup.Done()
for {
select {
case <-s.ch:
fmt.Println("stopping...")
return
default:
}

// Do something
}
}

func main() {

service := NewService()
go service.Serve()

// Handle SIGINT and SIGTERM.
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
fmt.Println(<-ch)

// Stop the service gracefully.
service.Stop()
}
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
邪灯是什么意思? 趣头条怎么解绑微信 趣头条APP解绑微信教程 趣头条怎么解绑微信号 趣头条怎样解除微信绑定 魔兽世界7.0古龙之陨怎么做 咸阳男子欲带孩子轻生被救回,成年人的生活有多不易? 陕西咸阳,一男子欲带孩子轻生被救回,轻生带孩子是什么操作? 我是帅哥,为什么还没有人追? 为什么我这么漂亮就是没有一个男人来追我 气死了? 人不在本地,怎么实名制 go语言 sleep 10秒 怎么实现 在go语言中select关键字怎么用? 详解Python中的协程,为什么说它的底层是生成器? sleep是什么意思 golang语言:for循环里面包含一个函数体的执行循序 go语言为什么我的程序不会执行子协程? go 语言 fmt.Println sleep不是实时打印 主持人发言稿 会议主持开场词该怎么说? 村委会会议主持人开场白台词怎么写? 中国古代的异姓王列表 ps2侠盗飞车罪恶都市VICE CITY 秘籍 琪和烨这两个字组成什么词语呢 OFF是关还是开 OFF代表开还是关 电闸上off是开还是关 水龙头开关方向标识no和 off那个是关水的 水阀这个状态是开着还是关着?请注意把手上的on/off标志。 水管阀门off指的是开还是关? 非你我所能企望啥意思? java的多线程在golang中是如何体现的? go to bed和go to sleep的区别 java程序中实现系统日志功能怎么做?就是用来记录使用系统的操作记录的那... 如何取消一个goroutine 的执行 怎么用就java 做日志分析啊 go to sleep 歌词的中文翻译 java定时读取日志文件 java日志分析 go to sleep Eminem 中文歌词 eminem的 go to sleep 的歌词中文翻译 java服务器日志保存到本地 He told me he could not go to sleep. That is what he _______. A said meBtalked to me C told to me java日志管理怎么获取到方法耗时 如何在JAVA中使用日志文件 java项目的后台管理中如何使用log4j来实现日志操作管理 java快速读取日志文件最后一行数据,日志文件非常大。 Java多打日志信息,会不会影响性能 Java日志??? 阿里云日志服务 怎么显示java异常日志信息 为什么我看快手对方叫视频我看不见