Golang 入门 : 等待 goroutine 完成任务
Goroutine 是 Golang 中非常有用的功能,但是在使用中我们经常碰到下面的场景:如果希望等待当前的 goroutine 执行完成,然后再接着往下执行,该怎么办?本文尝试介绍这类问题的解决方法。
没有等待的情况
让我们运行下面的代码,并关注输出的结果:
package main import (
"time"
"fmt"
) func say(s string) {
for i := ; i < ; i++ {
time.Sleep( * time.Millisecond)
fmt.Println(s)
}
} func main() {
go say("hello world")
fmt.Println("over!")
}
输出的结果为:
over!
因为 goroutine 以非阻塞的方式执行,它们会随着程序(主线程)的结束而消亡,所以程序输出字符串 "over!" 就退出了,这可不是我们想要的结果。
使用 Sleep 函数等待
要解决上面的问题,最简单、直接的方式就是通过 Sleep 函数死等 goroutine 执行完成:
func main() {
go say("hello world")
time.Sleep( * time.Millisecond)
fmt.Println("over!")
}
运行修改后的程序,结果如下:
hello world
hello world
hello world
over!
结果符合预期,但是太 low 了,我们不知道实际执行中应该等待多长时间,所以不能接受这个方案!
使用 channel
通过 channel 也可以达到等待 goroutine 结束的目的,运行下面的代码:
func main() {
done := make(chan bool)
go func() {
for i := ; i < ; i++ {
time.Sleep( * time.Millisecond)
fmt.Println("hello world")
}
done <- true
}()
<-done
fmt.Println("over!")
}
输出的结果也是:
hello world
hello world
hello world
over!
这种方法的特点是执行多少次 done <- true 就得执行多少次 <-done,所以也不是优雅的解决方式。
标准答案
Golang 官方在 sync 包中提供了 WaitGroup 类型来解决这个问题。其文档描述如下:
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 用来等待单个或多个 goroutines 执行结束。在主逻辑中使用 WaitGroup 的 Add 方法设置需要等待的 goroutines 的数量。在每个 goroutine 执行的函数中,需要调用 WaitGroup 的 Done 方法。最后在主逻辑中调用 WaitGroup 的 Wait 方法进行阻塞等待,直到所有 goroutine 执行完成。
使用方法可以总结为下面几点:
- 创建一个 WaitGroup 实例,比如名称为:wg
- 调用 wg.Add(n),其中 n 是等待的 goroutine 的数量
- 在每个 goroutine 运行的函数中执行 defer wg.Done()
- 调用 wg.Wait() 阻塞主逻辑
运行下面的代码:
package main import (
"time"
"fmt"
"sync"
) func main() {
var wg sync.WaitGroup
wg.Add()
say2("hello", &wg)
say2("world", &wg)
fmt.Println("over!")
} func say2(s string, waitGroup *sync.WaitGroup) {
defer waitGroup.Done() for i := ; i < ; i++ {
fmt.Println(s)
}
}
输出的结果如下:
hello
hello
hello
world
world
world
over!
下面是一个稍稍真实一点的例子,检查请求网站的返回状态。如果要在收到所有的结果后进一步处理这些返回状态,就需要等待所有的请求结果返回:
package main import (
"fmt"
"sync"
"net/http"
) func main() {
var urls = []string{
"https://www.baidu.com/",
"https://www.cnblogs.com/",
} var wg sync.WaitGroup for _, url := range urls {
wg.Add()
go fetch(url, &wg)
} wg.Wait()
} func fetch(url string, wg *sync.WaitGroup) (string, error) {
defer wg.Done()
resp, err := http.Get(url)
if err != nil {
fmt.Println(err)
return "", err
}
fmt.Println(resp.Status)
return resp.Status, nil
}
运行上面的代码,输出的结果如下:
200 OK
200 OK
参考:
How to Wait for All Goroutines to Finish Executing Before Continuing
Go WaitGroup Tutorial
Golang 入门 : 等待 goroutine 完成任务的更多相关文章
- Golang 入门 : goroutine(协程)
在操作系统中,执行体是个抽象的概念.与之对应的实体有进程.线程以及协程(coroutine).协程也叫轻量级的线程,与传统的进程和线程相比,协程的最大特点是 "轻"!可以轻松创建上 ...
- Golang 入门 : channel(通道)
笔者在<Golang 入门 : 竞争条件>一文中介绍了 Golang 并发编程中需要面对的竞争条件.本文我们就介绍如何使用 Golang 提供的 channel(通道) 消除竞争条件. C ...
- Golang 入门 : 竞争条件
笔者在前文<Golang 入门 : 理解并发与并行>和<Golang 入门 : goroutine(协程)>中介绍了 Golang 对并发的原生支持以及 goroutine 的 ...
- Golang入门(4):并发
摘要 并发程序指同时进行多个任务的程序,随着硬件的发展,并发程序变得越来越重要.Web服务器会一次处理成千上万的请求,这也是并发的必要性之一.Golang的并发控制比起Java来说,简单了不少.在Go ...
- Golang之chan/goroutine(转)
原文地址:http://tchen.me/posts/2014-01-27-golang-chatroom.html?utm_source=tuicool&utm_medium=referra ...
- Golang 探索对Goroutine的控制方法
前言 在golang中,只需要在函数调用前加上关键字go即可创建一个并发任务单元,而这个新建的任务会被放入队列中,等待调度器安排.相比系统的MB级别线程栈,goroutine的自定义栈只有2KB,这使 ...
- Java程序员的Golang入门指南(下)
Java程序员的Golang入门指南(下) 4.高级特性 上面介绍的只是Golang的基本语法和特性,尽管像控制语句的条件不用圆括号.函数多返回值.switch-case默认break.函数闭包.集合 ...
- Java程序员的Golang入门指南(上)
Java程序员的Golang入门指南 1.序言 Golang作为一门出身名门望族的编程语言新星,像豆瓣的Redis平台Codis.类Evernote的云笔记leanote等. 1.1 为什么要学习 如 ...
- 推荐一个GOLANG入门很好的网址
推荐一个GOLANG入门很好的网址,栗子很全 https://books.studygolang.com/gobyexample/
随机推荐
- Linux C++/C开发所必需的一系列工具
系统平台下的开发工具.开发环境各有不同.Linux C++/C开发所必需的一系列工具: 1. vi(vim)文本编辑器一个UNIX世界标准的文本编辑器,简约而强大,不论作为开发人员还是系统管理员,熟练 ...
- spring中常用的注解
使用注解来构造IoC容器 用注解来向Spring容器注册Bean.需要在applicationContext.xml中注册<context:component-scan base-package ...
- viewDidLoad、viewWillAppear、viewWillDisappear
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil viewDidLo ...
- Gentoo更新portage记录
小记一下这两天更新服务器版本遇到的各种问题. 服务器系统: Gentoo 第一天 其实本来不打算更新系统的,因为最近想试试免费的SSL证书,于是自然而然搜到了letsencrypt,跟着他们的流程需要 ...
- 【模拟】bzoj1686: [Usaco2005 Open]Waves 波纹
打完模拟题来庆祝一波:):感觉最近陷入一种“口胡五分钟打题两小时”的巨坑之中…… Description Input 第1行:四个用空格隔开的整数Pj Bi,B2,R. P(1≤P≤5)表示石 ...
- (42)zabbix使用IT services 了解服务器SLA整体情况
什么是IT Services 服务器或者某项服务.业务的可用率,不懂技术的上级领导会过问最近服务器可用率如何.所有api的状况怎么样? 通常一些技术人员会说负载怎么样,哪些cpu使用率怎么样,硬盘使用 ...
- Python面向对象(约束,异常处理,md5加密)(五)
1. 类的约束 1. 写一个父类. 父类中的某个方法要抛出一个异常 NotImplementedError class Base: def login(self): raise NotImplemen ...
- Python之路-基础数据类型之字典 集合
字典的定义-dict 字典(dict)是python中唯⼀的⼀个映射类型.他是以{ }括起来的键值对组成,字典是无序的,key是不可修改的.dic = {1:'好',2:'美',3:'啊'} 字典的操 ...
- PAT Basic 1050
1050 螺旋矩阵 本题要求将给定的 N 个正整数按非递增的顺序,填入“螺旋矩阵”.所谓“螺旋矩阵”,是指从左上角第 1 个格子开始,按顺时针螺旋方向填充.要求矩阵的规模为 m 行 n 列,满足条件: ...
- PHP获得网页源码
获取网页源代码: <?php $lines = file('http://www.hoverreader.com/'); foreach ($lines as $line_num => $ ...