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/
随机推荐
- javascript中typeof、undefined 和 null
typeof 是运算符,注意不是函数,是运算符,其作用,是考察变量究竟是什么类型.或曰,是变量是否定义或是否初始化的照妖镜.返回值是字符串. undefined 表示一个对象没有被定义或者没有被初始化 ...
- Spring框架 全注解annotation不使用配置文件(SpringConfiguration.java类代替) 补充 xml配置文件没有提示解决
全注解不使用配置文件 首先还是倒包 在原有的jar包: 需Spring压缩包中的四个核心JAR包 beans .context.core 和expression 下载地址: https://pan.b ...
- C#获得DataTable的key值
//获得dataTable的key值 List<string> keyList = new List<string>(); ; i < dt.Columns.Count; ...
- python数据类型、字符编码、文件处理
介绍: 1.什么是数据? 例:x=10,10是我们要存储的数据 2.为何数据要分不同的类型? 数据是用来表示状态的,不同的状态用不同的类型的数据去表示 1.数据类型 1.数字(整形,长整形,浮点型,复 ...
- 初中级PHP面试基础汇总
这是我整理的一套面试题,老铁们看看就当复习了哦 相关PHP面试题 搞定PHP面试 - 函数知识点整理 php 面试题目整理 PHP面试整理 PHP面试 概述 感觉现在发面试题有些冷门,就跟昨天德国那场 ...
- Elasticsearchs的安装/laravel-scout和laravel-scout-elastic的安装
安装: https://github.com/medcl/elasticsearch-rtf 先下载包 下载解压后 cd elasticsearch-rtf-master ll bin/elastic ...
- 牛客网暑期ACM多校训练营(第一场)J Different Integers(树状数组, 离线)
题意: 给定n个数字, 然后给出m个区间, 求区间外其他数字的种类有多少. 分析: 将区间以r为基准升序排序, 每次处理pre~r的数字第一次出现的位置. #include<bits/stdc+ ...
- DocView mode 1 -- 手册翻译
文档原文在线地址 * 35 Document Viewing** DocView mode is a major mode for viewing DVI, PostScript (PS), PDF, ...
- Java实现——Dom4j读写XML文件
1. dom4j概述 解析DOM4J是一个开源XML解析包,采用了Java集合框架并完全支持DOM,SAX和JAXP. 最大的特色是使用了大量的接口,主要接口都在org.dom4j里定义. 2. do ...
- struts 乱码
在进行struts开发的过程中,总也是出现很多的乱码问题 ,但归根到底,也只是以下三种情况: ㈠页面显示中文乱码 ㈡传递参数中文乱码 ㈢国际化资源文件乱码 下面就这三中情况介绍怎么在具体项目中处理这些 ...