利用通道(channel)的特性
对于golang的channel的基础有些了解的朋友都知道,channel主要有两类,一类是无缓冲,一类是有缓冲 。
1.无缓冲channel,接收方会一直阻塞直到有数据写入。发送方会一直阻塞直到接收方将数据取走。
2.带有缓冲区channel,发送方会一直阻塞直到数据被拷贝到缓冲区;如果缓冲区已满,则发送方只能在接收方取走数据后才能从阻塞状态恢复。
那么我们可以用无缓冲的Channel来实现当前等待goroutine的操作
func main() {
waitChan := make(chan int) //构建一个无缓冲的Channel
goroutineCount := 10
for i := 0; i < goroutineCount; i++ {
go func(tag int) {
do(tag)
waitChan <- 1 //往通道写入数据
}(i)
}
goroutineDoneCount := 0
for range waitChan { //阻塞等待数据写入
goroutineDoneCount++
if goroutineDoneCount == goroutineCount {
//收到与开启goroutineCount相等时跳出循环
break
}
}
close(waitChan)
fmt.Println("All goroutine finish")
}
func do(tag int) {
fmt.Printf("Do by tag[%d] \n", tag)
}
输出结果:
Do by tag[9]
Do by tag[2]
Do by tag[3]
Do by tag[4]
Do by tag[7]
Do by tag[8]
Do by tag[0]
Do by tag[6]
Do by tag[5]
Do by tag[1]
All goroutine finish
当然channel阻塞并不是最佳方案,首先需要知道确定个数的goroutine,同时稍不注意就极易产生死锁。
优雅利用sync.WaitGroup
使用go标准库sync,其中提供了专门的解决方案sync.WaitGroup 用于等待一个goroutines集合的结束.
func main() {
goroutineWaitGroup := sync.WaitGroup{} //构建一个waitGroup
goroutineCount := 10
for i := 0; i < goroutineCount; i++ {
goroutineWaitGroup.Add(1) //添加WaitGroup计数器
go func(tag int) {
defer goroutineWaitGroup.Done() //defer标记当前函数作用域执行结束后 释放一个计数器
do(tag)
}(i)
}
goroutineWaitGroup.Wait() //阻塞,直到WaitGroup中的计数器为0
fmt.Println("All goroutine finish")
}
func do(tag int) {
fmt.Printf("Do by tag[%d] \n", tag)
}