前言
上一篇文章简单介绍了一下 golang
实现协程的方法:
上次提到过一个问题,由于创建一个新的 goroutine
需要时间,所以导致出现了一个问题就是,go
出来的函数还没有运行,而 main
函数却已经运行完了,主函数结束,子函数也就结束了,所以子函数没有时间运行,上一篇文章使用的休眠来让 go
出来的子函数有足够的时间运行完成,但是我们在实际的开发中,并不能够知道子函数会用多少时间,我们该留下多少时间让子函数运行,所以很明显,休眠是不正确
解决办法
这里要用到一个库:syncsync
包提供了一个类型:WaitGroup
实现也很简单:
var 名称 sync.WaitGroup
这里WaitGroup提供了三个方法:
- Add:当初始化完成时,会有一个计数器,初始为0,调用Add方法后,计数器+1
- Done:调用此方法后,计数器-1
- Wait:等待阻塞当前进程,等待计数器为0时,继续执行
所以通过这三个方法,我们可以大致清楚是怎么控制协程的
- 主函数创建
WaitGroup
实列 - 每创建一个子函数,调用Add方法将计数器+1,并将WaitGroup传入子函数,注意此处,以引用的方法传入,避免发生副本拷贝而造成死锁
- 在子函数功能实现完成后,调用Done方法,将计数器-1,说明一个协程执行完毕,可以继续
- 在主函数中调用Wait方法,表示要等待计数器归零才能继续执行
看代码:
package main
import (
"fmt"
"sync"
)
func gohandle(name string, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Printf("In goroutine %s\n", name)
}
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
str := fmt.Sprintf("协程%d号", i+1)
go gohandle(str, &wg)
}
wg.Wait()
}
这样我们就不用再用休眠来是完成了
协程数量
前面说了,怎么优雅的等待协程执行完成,那么更近一步,控制协程的数量这时候就要用到信道+WaitGroup来实现
代码:
package main
import (
"fmt"
"strconv"
"sync"
"time"
)
func handle(data string, ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println(data)
time.Sleep(time.Second)
<-ch
}
func main() {
ch := make(chan int, 3)
var wg sync.WaitGroup
datas := [10]string{}
for i := 0; i < 10; i++ {
datas[i] = strconv.Itoa(i)
}
for i := 0; i < len(datas); i++ {
ch <- 1
wg.Add(1)
go handle(datas[i], ch, &wg)
}
wg.Wait()
}
这里使用有缓冲区信道来控制协程的数量,由于信道的数量呗固定了,所以必须等待信道中有空位时,下一个协程才能继续进行
1 条评论
滴!学生卡!打卡时间:下午3:38:21,请上车的乘客系好安全带~