转自:https://www.cnblogs.com/cobbliu/p/4461866.html

goroutine和channel是Go语言非常棒的特色,它们提供了一种非常轻便易用的并发能力。但是当您的应用进程中有很多goroutine的时候,如何在主流程中等待所有的goroutine 退出呢?

1 通过Channel传递退出信号

Go的一大设计哲学就是:通过Channel共享数据,而不是通过共享内存共享数据。主流程可以通过channel向任何goroutine发送停止信号,就像下面这样:

func run(done chan int) {
for {
select {
case <-done:
fmt.Println("exiting...")
done <-
break
default:
} time.Sleep(time.Second * )
fmt.Println("do something")
}
} func main() {
c := make(chan int) go run(c) fmt.Println("wait")
time.Sleep(time.Second * ) c <-
<-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:

  1. 创建一个Waitgroup的实例,假设此处我们叫它wg
  2. 在每个goroutine启动的时候,调用wg.Add(1),这个操作可以在goroutine启动之前调用,也可以在goroutine里面调用。当然,也可以在创建n个goroutine前调用wg.Add(n)
  3. 当每个goroutine完成任务后,调用wg.Done()
  4. 在等待所有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()
defer s.waitGroup.Done() for {
select {
case <-s.ch:
fmt.Println("stopping...")
return
default:
}
s.waitGroup.Add()
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()
}

是不是方便优雅多了?

如何优雅地等待所有的goroutine退出的更多相关文章

  1. golang如何使用channel控制goroutine退出

    最经典的处理方式: 在启动goroutine的时候,传递一个额外的chan型参数,用来接收退出信号,代码如下 func worker(name string, stopchan chan struct ...

  2. Java父线程(或是主线程)等待所有子线程退出

    static void testLock1(){ final AtomicInteger waitCount = new AtomicInteger(30000); final Object wait ...

  3. 如何优雅的控制goroutine的数量

    1,为什么要控制goroutine的数量? goroutine固然好,但是数量太多了,往往会带来很多麻烦,比如耗尽系统资源导致程序崩溃,或者CPU使用率过高导致系统忙不过来.比如: ; i < ...

  4. goroutine并发控制与通信

    转发:https://mp.weixin.qq.com/s/ZlyQHfmoY1lzOoRgFSCOBw 开发go程序的时候,时常需要使用goroutine并发处理任务,有时候这些goroutine是 ...

  5. go 并发编程(2)

    协程 执行体是个抽象的概念,在操作系统层面有很多个概念与之对应,如操作系统自己掌管的进程(process),进程内的线程(thread),以及进程内的协程(coroutine,也叫轻量级线程).与传统 ...

  6. go中waitGroup源码解读

    waitGroup源码刨铣 前言 WaitGroup实现 noCopy state1 Add Wait 总结 参考 waitGroup源码刨铣 前言 学习下waitGroup的实现 本文是在go ve ...

  7. Golang 入门 : 等待 goroutine 完成任务

    Goroutine 是 Golang 中非常有用的功能,但是在使用中我们经常碰到下面的场景:如果希望等待当前的 goroutine 执行完成,然后再接着往下执行,该怎么办?本文尝试介绍这类问题的解决方 ...

  8. 【Golang】程序如何优雅的退出?

    1. 背景 项目开发过程中,随着需求的迭代,代码的发布会频繁进行,在发布过程中,如何让程序做到优雅的退出? 为什么需要优雅的退出? 你的 http 服务,监听端口没有关闭,客户的请求发过来了,但处理了 ...

  9. windows主线程等待子线程退出卡死问题

    在windows下调用_beginthread创建子线程并获得子线程id(函数返回值),如果子线程很快退出,在主线程中调用WaitForSingleObject等待该线程id退出,会导致主线程卡死.需 ...

随机推荐

  1. ksonnet 一个简化编写以及部署kubernetes的工具

    ksonnet 是一个基于jsonnet的快速简化kubernetes yaml 配置的工具,可以实现配置的复用 同时也包含一个registry 的概念,可以实现可复用组件的分发,同时支持helm 环 ...

  2. C# to IL 4 Keywords and Operators(关键字和操作符)

    Code that is placed after the return statement never gets executed. In the first programgiven below, ...

  3. S老师 C#编程数据结构篇 学习

    直接插入排序                                                       冒泡排序 简单选择排序 线性表: using System; using Sy ...

  4. 设置 IntelliJ IDEA 的彩色代码主题

    首先,给出一系列 IntelliJ IDEA 代码的彩色主题,供大家选择: VibrantUnknown(Darcula) FadeComments NicePython Solarized Have ...

  5. Linux基础入门之vsFTP+MySQL/MariaDB认证实现虚拟用户配置详解

    https://www.dwhd.org/20150603_144841.html 摘要 VSFTP可以使用系统账户或者匿名账户登录,但是出于安全的考虑,通常建议使用vsftp虚拟账户来登录ftp服务 ...

  6. ASP.NET AJAX入门系列(2):使用ScriptManager控件

    ScriptManager控件包括在ASP.NET 2.0 AJAX Extensions中,它用来处理页面上的所有组件以及页面局部更新,生成相关的客户端代理脚本以便能够在JavaScript中访问W ...

  7. 【java】标示符

    java的标示符由数字0-9,字母a-zA-Z,_$组成.(java是严格区分大小写的) 标示符的规则: 1.不能以数字开头 2.不能使用关键字 一般的命名规则: 包名:多个单词组成时,所有字母全部小 ...

  8. 1、Dreamweaver+php开发网站第一步

    1.首先在appserv目录下的www中建立一个网站文件夹,例如test 2.在Dreamweaver中的站点下新建站点进行配置,其中站点选项和服务器选项都要配置. 3.然后在Dreamweaver中 ...

  9. 关于opcdaauto.dll的注册

    关于opcdaauto.dll的注册 无论win7_32还是win7_64位都执行一样的CMD命令,即regsvr32 opcdaauto.dll . 如果从网上下载的opcdaauto.dll 文件 ...

  10. jenkins构建触发器详解-不登录触发远程构建

    利用jenkins的远程构建功能,我们可以使用任何脚本,甚至定制一个Web页来控制Job的执行,但是远程构建你如果直接使用的话,老是需要登录才能执行,如何避免登录?稍微折腾了一下,调通了. 1.首先去 ...