Golang 同步等待组(WaitGroup)

如果你正在学习Go的高性能并发应用开发,那么了解同步等待组至关重要。本文带你认识同步等待组并通过示例进行说明。

1. 同步等待组(WaitGroup)

让我们直入主题,说明是同步等待组(WaitGroup),能够解决什么问题。

在实际使用Go协程实现并行应用时,可能会遇到这样场景:需要阻塞部分代码执行,直到其他协程成功执行之后才继续执行。
示例代码:

package mainimport "fmt"func myFunc() {fmt.Println("Inside my goroutine")
}func main() {fmt.Println("Hello World")go myFunc()fmt.Println("Finished Execution")
}

程序首先打印"Hello World",接着启动协程,最后打印"Finished Execution"。
但我们执行程序结果并不是我们预期的结果,协程内的信息"Inside my goroutine"并没有出现。这是因为main在协程执行之前以及结束,所以协程中的逻辑并未执行。

如何解决————同步等待组(WaitGroups)

同步等待组(WaitGroups)就是要解决这类问题,阻塞应用直到同步等待组中的所有协程都成功执行。
首先调用同步等待组的Add(1)方法,设置需要等待协程数量, 然后再协程内部调用Done() 方法表明协程执行结束。

注意,需要确保再执行协程之前调用Add(1)方法。

2. 示例

掌握了一些基本概念后,下面通过示例展示如何通过同步等待组解决上述问题:

package mainimport ("fmt""sync"
)func myFunc(waitgroup *sync.WaitGroup) {fmt.Println("Inside my goroutine")waitgroup.Done()
}func main() {fmt.Println("Hello World")var waitgroup sync.WaitGroupwaitgroup.Add(1)go myFunc(&waitgroup)waitgroup.Wait()fmt.Println("Finished Execution")
}

我们看到首先实例化sync.WaitGroup,然后再执行协程之前调用Add(1)方法。修改原来函数增加*sync.WaitGroup参数,并在函数内部成功完成任务后调用一次Done方法。最后调用waitgroup.Wait()方法阻塞main函数执行,直到同步等待组中的协程成功执行完成。

下面再次运行程序输出结果如下:

Hello World
Inside my goroutine
Finished Execution
  • 匿名函数

我们也可以使用匿名函数实现相同功能。对于协程内部业务不复杂,匿名函数会让程序更简洁:

package mainimport ("fmt""sync"
)func main() {fmt.Println("Hello World")var waitgroup sync.WaitGroupwaitgroup.Add(1)go func() {fmt.Println("Inside my goroutine")waitgroup.Done()}()waitgroup.Wait()fmt.Println("Finished Execution")
}

输出结果一样。对于稍微复杂的逻辑,可能需要给匿名函数传入参数,例如需要传入url参数:

go func(url string) {fmt.Println(url)
}(url)

只是写法有点差异,其他都差不多。

3. 实战应用

在示例生产应用程序中,任务是创建一个API,该API与大量其他API交互,并将结果聚合到一个响应中。每个API调用大约花费2-3秒时间来返回响应,由于需要调用的API数量太多,不可能同步地进行此操作。

为了实现该功能,需要使用协程异步执行这些请求。

package mainimport ("fmt""log""net/http"
)var urls = []string{"https://baidu.com","https://csdn.net","https://qq.com",
}func fetch(url string) {resp, err := http.Get(url)if err != nil {fmt.Println(err)}fmt.Println(resp.Status)
}func homePage(w http.ResponseWriter, r *http.Request) {fmt.Println("HomePage Endpoint Hit")for _, url := range urls {go fetch(url)}fmt.Println("Returning Response")fmt.Fprintf(w, "All Responses Received")
}func handleRequests() {http.HandleFunc("/", homePage)log.Fatal(http.ListenAndServe(":8081", nil))
}func main() {handleRequests()
}

然而当我开始使用这种策略时,我注意到我对任何API调用都在协程有机会完成填充结果之前返回。这时需要使用同步等待组重新实现该功能,通过使用WaitGroup,我可以有效地修复这种异常行为,并且只在所有goroutines完成后返回结果。

package mainimport ("fmt""log""net/http""sync"
)var urls = []string{"https://baidu.com","https://csdn.net","https://qq.com",
}func fetch(url string, wg *sync.WaitGroup) (string, error) {resp, err := http.Get(url)if err != nil {fmt.Println(err)return "", err}wg.Done()fmt.Println(resp.Status)return resp.Status, nil
}func homePage(w http.ResponseWriter, r *http.Request) {fmt.Println("HomePage Endpoint Hit")var wg sync.WaitGroupfor _, url := range urls {wg.Add(1)go fetch(url, &wg)}wg.Wait()fmt.Println("Returning Response")fmt.Fprintf(w, "Responses")
}func handleRequests() {http.HandleFunc("/", homePage)log.Fatal(http.ListenAndServe(":8081", nil))
}func main() {handleRequests()
}

现在我们增加同步等待锁,它将对所有URL执行HTTP GET请求,一旦执行完毕返回给调用客户端。输出结果为:

HomePage Endpoint Hit
200 OK
200 OK
200 OK
Returning Response

处理这类问题的方法不止一种,通过使用Golang的通道也可以实现类似功能。

4. 总结

本文学习了什么是Golang同步等待组以及如何使用它实现高性能应用。

Golang 同步等待组(WaitGroup)相关推荐

  1. Golang gorouting 并发控制 sync.WaitGroup 介绍与使用

    WaitGroup简称同步组,用于等待goroutines结束的. 官方文档: type WaitGroupA WaitGroup waits for a collection of goroutin ...

  2. golang中的sync.WaitGroup

    golang中的sync.WaitGroup Posted on 2015/04/09刚才看golang的sync的包,看见一个很有用的功能.就是WaitGroup. 先说说WaitGroup的用途: ...

  3. 同步等待 异步等待_异步/等待和承诺的解释

    同步等待 异步等待 The async / await operators make it easier to implement many async Promises. They also all ...

  4. 同步等待异步操作,为什么Wait()在这里冻结程序

    本文翻译自:Synchronously waiting for an async operation, and why does Wait() freeze the program here Pref ...

  5. Linux信号 四 异步等待信号与同步等待信号接口

    信号的同步等待和异步等待区别就是信号处理函数的执行与否,异步等待是信号处理函数已经执行了,同步等待是信号处理函数还没有执行. 异步等待接口:pause() 和 sigsuspend() 1. paus ...

  6. 同步等待 异步等待_异步/等待与承诺互操作性

    同步等待 异步等待 Usually, when discussing Promises and async/await syntax, people frame it as an "eith ...

  7. 线程同步——内核对象实现线程同步——等待函数

    1 对于内核对象实现线程同步,不得不提三点: 2 1)大多数内核对象既有触发也有未触发两个状态 3 比如:进程.线程.作业.文件流.事件.可等待的计时器.信号量.互斥量 4 2)等待函数:等待函数使线 ...

  8. Kafka如何对Topic元数据进行细粒度的懒加载、同步等待?

    首先确保Topic的元数据可用,否则消息根本没法往外发.如果以前从没加载过Topic元数据,就会在doSend发送消息时调用waitOnMetadata方法在此同步阻塞住,等待连接Broker成功后拉 ...

  9. Golang同步锁的两种方式

    1 原子锁 可以借助某个信号向所有的goroutine 发送消息 var (shotdown int64 // 该标志向多个goroutine通知状态wg sync.WaitGroup )func m ...

最新文章

  1. 子页面跳转到父页面指定地方
  2. mysql 用户管理和权限设置
  3. 设备无法获得谷歌运行怎么办_因此,您刚刚获得了Google主页。 怎么办?
  4. < meta name=“viewport“ content=“width=device-width, initial-scale=1.0“>的解释
  5. Linux 文件压缩解压缩
  6. Pytorch深度学习实战项目回顾
  7. 一个核心系统 3 万多行代码的重构之旅
  8. java面试题整理_2018年最新java面试题整理。。。持续更新中。。。
  9. mysql int 拼接_MySQL 修改int类型为bigint SQL语句拼接
  10. MySQL Query Cache 小结
  11. idea面板右下角的切换分支找不到
  12. php还原时间戳,如何将php时间戳转换回日期
  13. NiFi 一键自动升级Nar包
  14. 携宠旅行逐渐成为新一代宠物主“刚需”,宠物友好型酒店呈现稀缺
  15. tm影像辐射定标_「教程」遥感图像预处理之辐射定标
  16. 最高100,000美元大奖,2021腾讯广告算法大赛开启
  17. cpp(c++)基础
  18. 论文阅读 [TPAMI-2022] VolterraNet: A Higher Order Convolutional Network With Group Equivariance for Homo
  19. 经典怀旧FCgame红白机小游戏在线网页合集版畅玩HTML网站源码
  20. 游戏公司如何应对游戏黑产 ?

热门文章

  1. java-net-php-python-jspm点餐管理系统计算机毕业设计程序
  2. openpyxl学习笔记(2020--8-11)——制图续
  3. 如何使用python的openpyxl进行强大的图表处理
  4. RS232、RS485及RS422有什么区别
  5. vue运行(Emitted value instead of an instance of Error)
  6. python绘制图形界面(一)
  7. mysql数据库的主从同步(主服务器存在内网IP)
  8. 几分之几在手机计算机上是哪个符号,数学各 种符号怎么表达比如根号,几分之几 – 手机爱问...
  9. plot_route(pos_r); matlab,蚁群算法 matlab程序(已运行) - lyp2003ok的专栏 - CSDN博客
  10. oracle12c生命周期,Oracle 12c 新特性之: ILM 数据生命周期管理