从零学习 Go 语言(27):学习 Go 协程中的 WaitGroup

hello_wbm · · 922 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

![](http://image.iswbm.com/20200607145423.png) 在线博客:http://golang.iswbm.com/ Github:https://github.com/iswbm/GolangCodingTime --- 在前两篇文章里,我们学习了 `协程` 和 `信道` 的内容,里面有很多例子,当时为了保证 main goroutine 在所有的 goroutine 都执行完毕后再退出,我使用了 time.Sleep 这种简单的方式。 由于写的 demo 都是比较简单的, sleep 个 1 秒,我们主观上认为是够用的。 但在实际开发中,开发人员是无法预知,所有的 goroutine 需要多长的时间才能执行完毕,sleep 多了吧主程序就阻塞了, sleep 少了吧有的子协程的任务就没法完成。 因此,使用time.Sleep 是一种极不推荐的方式,今天主要就要来介绍 一下如何优雅的处理这种情况。 ## 1. 使用信道来标记完成 > “不要通过共享内存来通信,要通过通信来共享内存” 学习了信道后,我们知道,信道可以实现多个协程间的通信,那么我们只要定义一个信道,在任务完成后,往信道中写入true,然后在主协程中获取到true,就认为子协程已经执行完毕。 ```go import "fmt" func main() { done := make(chan bool) go func() { for i := 0; i < 5; i++ { fmt.Println(i) } done <- true }() <-done } ``` 输出如下 ``` 0 1 2 3 4 ``` ## 2. 使用 WaitGroup 上面使用信道的方法,在单个协程或者协程数少的时候,并不会有什么问题,但在协程数多的时候,代码就会显得非常复杂,有兴趣可以自己尝试一下。 那么有没有一种更加优雅的方式呢? 有,这就要说到 sync包 提供的 WaitGroup 类型。 WaitGroup 你只要实例化了就能使用 ```go var 实例名 sync.WaitGroup ``` 实例化完成后,就可以使用它的几个方法: - Add:初始值为0,你传入的值会往计数器上加,这里直接传入你子协程的数量 - Done:当某个子协程完成后,可调用此方法,会从计数器上减一,通常可以使用 defer 来调用。 - Wait:阻塞当前协程,直到实例里的计数器归零。 举一个例子: ```go import ( "fmt" "sync" ) func worker(x int, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 5; i++ { fmt.Printf("worker %d: %d\n", x, i) } } func main() { var wg sync.WaitGroup wg.Add(2) go worker(1, &wg) go worker(2, &wg) wg.Wait() } ``` 输出如下 ``` worker 2: 0 worker 2: 1 worker 2: 2 worker 2: 3 worker 2: 4 worker 1: 0 worker 1: 1 worker 1: 2 worker 1: 3 worker 1: 4 ``` 以上就是我们在 Go 语言中实现一主多子的协程协作方式,推荐使用 sync.WaitGroup。。 ## 系列导读 --- [从零学习 Go 语言(01):一文搞定开发环境的搭建](https://studygolang.com/articles/27365) [从零学习 Go 语言(02):学习五种变量创建的方法](https://studygolang.com/articles/27432) [从零学习 Go 语言(03):数据类型之整型与浮点型](https://studygolang.com/articles/27440) [从零学习 Go 语言(04):byte、rune与字符串](https://studygolang.com/articles/27463) [从零学习 Go 语言(05):数据类型之数组与切片](https://studygolang.com/articles/27508) [从零学习 Go 语言(06):数据类型之字典与布尔类型](https://studygolang.com/articles/27563) [从零学习 Go 语言(07):数据类型之指针](https://studygolang.com/articles/27585) [从零学习 Go 语言(08):流程控制之if-else](https://studygolang.com/articles/27613) [从零学习 Go 语言(09):流程控制之switch-case](https://studygolang.com/articles/27660) [从零学习 Go 语言(10):流程控制之for 循环](https://studygolang.com/articles/28120) [从零学习 Go 语言(11):goto 无条件跳转](https://studygolang.com/articles/28472) [从零学习 Go 语言(12):流程控制之defer 延迟语句](https://studygolang.com/articles/28515) [从零学习 Go 语言(13):异常机制 panic 和 recover](https://studygolang.com/articles/28519) [从零学习 Go 语言(14):Go 语言中的类型断言是什么?](https://studygolang.com/articles/29305) [从零学习 Go 语言(15):学习 Go 语言的结构体与继承](https://studygolang.com/articles/29306) [从零学习 Go 语言(17):Go 语言中的 make 和 new 有什么区别?](https://studygolang.com/articles/29315) [从零学习 Go 语言(18):Go 语言中的 语句块与作用域](https://studygolang.com/articles/29365) [从零学习 Go 语言(19):Go Modules 前世今生及入门使用](https://studygolang.com/articles/29371) [从零学习 Go 语言(20):关于包导入必学的 8 个知识点](https://studygolang.com/articles/29404) [从零学习 Go 语言(21):一文了解 Go语言中编码规范](https://studygolang.com/articles/29477) [从零学习 Go 语言(22):Go 语言中如何开源自己写的包给别人用?](https://studygolang.com/articles/29609) [从零学习 Go 语言(23):一篇文章搞懂 Go 语言的函数](https://studygolang.com/articles/29628) [从零学习 Go 语言(24):理解 Go 语言中的 goroutine](https://studygolang.com/articles/29641) [从零学习 Go 语言(25):详解信道/通道](https://studygolang.com/articles/29704) [从零学习 Go 语言(26):通道死锁经典错误案例详解](https://studygolang.com/articles/29756) --- ![](http://image.python-online.cn/20200321153457.png)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

922 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传