转自: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. python实现单链表的翻转

    #!/usr/bin/env python #coding = utf-8 class Node:     def __init__(self,data=None,next = None):      ...

  2. Tomcat环境变量配置命令行报错:The JRE_HOME environment variable is not defined correctl This environment variable is needed to run this program

    1. tomcat——>bin——>setclasspath.bat,使用记事本打开. 2. 添加如下代码即可: 为自己实际的环境变量配置为准!!! set JAVA_HOME=D:\ID ...

  3. MySQL--Checkpoint基础

    ===================================================== Checkpint 分两种:Sharp Checkpoint : 在服务器正常关闭时,将所有 ...

  4. spark aggregate函数详解

    aggregate算是spark中比较常用的一个函数,理解起来会比较费劲一些,现在通过几个详细的例子带大家来着重理解一下aggregate的用法. 1.先看看aggregate的函数签名在spark的 ...

  5. pushpin openresty 集成试用

    pushpin 是一个很不错的将restapi 转换为reailtime api 的proxy,openresty 具有很强的nginx 控制能力 可以方便的用来进行api 的开发,默认其他语言pus ...

  6. kettle的系列教程

    kettle--window开发环境和linux运行环境的迁移 kettle的输入输出组件和脚本组件 kettle--变量的使用 kettle的资源库 kettle的job kettle的hello ...

  7. MySQL插入,更新,删除数据

    插入 单行插入 1.insert into 表名 values(col1_value,col2_value,...); 每个列必须提供一个值,如果没有值,要提供NULL值 每个列必须与它在表中定义的次 ...

  8. DefinePlugin插件

    这个插件允许你创建全局常量用于编译时解析.如果设置mode:"production",webpack默认会设置"process.env.NODE_ENV": J ...

  9. prop-types:该第三方库对组件的props中的变量进行类型检测

    利用prop-types第三方库对组件的props中的变量进行类型检测

  10. py-day2 python 列表

    1.列表的基本格式: 2.列表中可以嵌套任何类型: 中括号括起来.逗号分割每个元素. 列表中的元素可以是:数字/字符串/列表/布尔值 等等,所有的都能放进去. 集合内部可以放置任何东西. 3.通过索引 ...