您的位置:首页 > 大数据 > 人工智能

Go并发:利用sync.WaitGroup实现协程同步

2017-05-25 09:28 337 查看
经常看到有人会问如何等待主协程中创建的协程执行完毕之后再结束主协程,例如如下代码:

package main

import (
"fmt"
)

func main() {
go func() {
fmt.Println("Goroutine 1")
}()

go func() {
fmt.Println("Goroutine 2")
}()
}


执行以上代码很可能看不到输出,因为有可能这两个协程还没得到执行主协程已经结束了,而主协程结束时会结束所有其他协程。解决办法是可以在main函数结尾加上等待:

package main

import (
"fmt"
"time"
)

func main() {
go func() {
fmt.Println("Goroutine 1")
}()

go func() {
fmt.Println("Goroutine 2")
}()

time.Sleep(time.Second * 1) // 睡眠1秒,等待上面两个协程结束
}


这并不是完美的解决方法,如果这两个协程中包含复杂的操作,可能很耗时间,就无法确定需要睡眠多久,当然可以用管道实现同步:

package main

import (
"fmt"
)

func main() {

ch := make(chan struct{})
count := 2 // count 表示活动的协程个数

go func() {
fmt.Println("Goroutine 1")
ch <- struct{}{} // 协程结束,发出信号
}()

go func() {
fmt.Println("Goroutine 2")
ch <- struct{}{} // 协程结束,发出信号
}()

for range ch {
// 每次从ch中接收数据,表明一个活动的协程结束
count--
// 当所有活动的协程都结束时,关闭管道
if count == 0 {
close(ch)
}
}
}


上面的解决方案是比较完美的方案,但是Go提供了更简单的方法——使用sync.WaitGroup。WaitGroup顾名思义,就是用来等待一组操作完成的。WaitGroup内部实现了一个计数器,用来记录未完成的操作个数,它提供了三个方法,Add()用来添加计数。Done()用来在操作结束时调用,使计数减一。Wait()用来等待所有的操作结束,即计数变为0,该函数会在计数不为0时等待,在计数为0时立即返回。

package main

import (
"fmt"
"sync"
)

func main() {

var wg sync.WaitGroup

wg.Add(2) // 因为有两个动作,所以增加2个计数
go func() {
fmt.Println("Goroutine 1")
wg.Done() // 操作完成,减少一个计数
}()

go func() {
fmt.Println("Goroutine 2")
wg.Done() // 操作完成,减少一个计数
}()

wg.Wait() // 等待,直到计数为0
}


可见用sync.WaitGroup是最简单的方式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: