会员登录 - 用户注册 - 设为首页 - 加入收藏 - 网站地图 一篇学会Go并发等待!

一篇学会Go并发等待

时间:2025-11-04 00:14:35 来源:益强数据堂 作者:系统运维 阅读:654次

上节答疑

上一节有读者问goroutine stack size一般是篇学多大,我进行了详细的篇学查询

关于 goroutine stack size(栈内存大小) 官方的文档 中所述,1.2 之前最小是篇学4kb,在1.2 变成8kb,篇学并且可以使用SetMaxStack 设置栈最大大小。篇学

在 runtime/debug 包能控制最大的篇学单个 goroutine 的堆栈的大小。在 64 位系统上默认为 1GB,篇学在 32 位系统上默认为 250MB。篇学

因为每个goroutine需要能够运行,篇学所以它们都有自己的篇学栈。假如每个goroutine分配固定栈大小并且不能增长,篇学太小则会导致溢出,篇学太大又会浪费空间,篇学无法存在许多的篇学goroutine。

所以在1.3版本中,篇学改为了 Contiguous stack( 连续栈 ),为了解决这个问题,goroutine可以初始时只给栈分配很小的云南idc服务商空间(8KB),然后随着使用过程中的需要自动地增长。这就是为什么Go可以开千千万万个goroutine而不会耗尽内存。

1.4 版本 goroutine 堆栈从 8Kb 减少到 2Kb

Golang并发等待

本节源码位置 https://github.com/golang-minibear2333/golang/blob/master/4.concurrent/goroutine-wait/”

简介

goroutine 是 Golang 中非常有用的功能,有时候 goroutine 没执行完函数就返回了,如果希望等待当前的 goroutine 执行完成再接着往下执行,该怎么办?

func say(s string) {     for i := 0; i < 3; i++ {         time.Sleep(100 * time.Millisecond)         fmt.Println(s)     } } func main() {     go say("hello world")     fmt.Println("over!") } 

输出 over! , 主线程没有等待

使用 Sleep 等待

func main() {     go say("hello world")     time.Sleep(time.Second*1)     fmt.Println("over!") } 

运行修改后的程序,结果如下:

hello world hello world hello world over! 

结果符合预期,但是太 low 了,我们不知道实际执行中应该等待多长时间,所以不能接受这个方案!

发送信号

func main() {     done := make(chan bool)     go func() {         for i := 0; i < 3; i++ {             time.Sleep(100 * time.Millisecond)             fmt.Println("hello world")         }         done <- true     }()     <-done     fmt.Println("over!") } 

输出的结果和上面相同,也符合预期

这种方式不能处理多个协程,所以也不是优雅的解决方式。

WaitGroup

Golang 官方在 sync 包中提供了 WaitGroup 类型可以解决这个问题。其文档描述如下:

使用方法可以总结为下面几点:

在父协程中创建一个 WaitGroup 实例,比如名称为:wg 调用 wg.Add(n) ,云服务器提供商其中 n 是等待的 goroutine 的数量 在每个 goroutine 运行的函数中执行 defer wg.Done() 调用 wg.Wait() 阻塞主逻辑 直到所有 goroutine 执行完成。 func main() {     var wg sync.WaitGroup     wg.Add(2)     go say2("hello", &wg)     go say2("world", &wg)     fmt.Println("over!")     wg.Wait() } func say2(s string, waitGroup *sync.WaitGroup) {     defer waitGroup.Done()     for i := 0; i < 3; i++ {         fmt.Println(s)     } } 

输出,注意顺序混乱是因为并发执行

hello hello hello over! world world world 

小心缺陷

简短的例子,注意循环传入的变量用中间变量替代,防止闭包 bug

func errFunc() {  var wg sync.WaitGroup  sList := []string{"a", "b"}  wg.Add(len(sList))  for _, d := range sList {   go func() {    defer wg.Done()    fmt.Println(d)   }()  }  wg.Wait() } 

输出,可以发现全部变成了最后一个

b b 

父协程与子协程是并发的。父协程上的for循环瞬间执行完了,内部的协程使用的是d最后的值,这就是闭包问题。

解决方法当作参数传入

func correctFunc() {  var wg sync.WaitGroup  sList := []string{"a", "b"}  wg.Add(len(sList))  for _, d := range sList {   go func(str string) {    defer wg.Done()    fmt.Println(str)   }(d)  }  wg.Wait() } 

输出

b a 

要留意 range 中的value有可能出现 1.7.3 有可能会遇到的香港云服务器坑!

(责任编辑:系统运维)

推荐内容
  • 通过激情探索学习(以passion为引导,最大化教程的效果与体验)
  • 戴尔服务器管理工具iDRAC、OpenManagement Enterprise、CloudIQ介绍
  • 无线不设限丨新华三无线WA6500系列AP全面升级
  • 2022年第十七届中国企业年终评选榜单揭晓:联想ST650 V2服务器荣获2022年度中国IT行业人工智能优秀产品奖
  • 华为Watch智能手表的功能与体验(探究华为Watch智能手表的特色功能和用户体验)
  • 施耐德电气发布新一代模块化三相UPS Galaxy PX:为中小型数据中心和其他关键业务应用保驾护航