我们都知道,在golang中很容易实现并发,只需要一个关键字go
就可以。但是在开启了多个goruntine之后,我们要如何去管理它们呢(包括停止退出goroutine,等待goruntine执行完成,继续让goruntine执行等)。这些我们在日常的业务中都可能碰到,下面就讲讲在不同的场景如何正确优雅地管理goruntine,即控制并发。
一,sync.WaitGroup
作用: 任务编排,等待多个 goroutine 全部完成
适用场景: 好多个 goroutine 协同做一件事情的时候,因为每个 goroutine 做的都是这件事情的一部分,只有全部的 goroutine 都完成,这件事情才算是完成,这是等待的方式。
func main() {
var wg
(2)
go func() {
(2 * )
("1号完成")
()
}()
go func() {
(2 * )
("2号完成")
()
}()
()
("好了,大家都干完了,放工")
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
这部分比较简单,不过多描述。更多参见在异步读写中使用
二,
上面的场景是等待多个小任务都执行完毕之后退出。下面考虑这种场景:有一个持续性的任务,一般是常驻的不退出,比如说监控任务。但有时可能因为特殊需求,需要根据条件判断后让其退出。因为该监控任务和其他任务没有数据连接(channel之类的),但靠是不能使其退出的。 很自然的,我们能想到应该通过一个媒介,来通知监控任务退出。全局变量可以充当这个媒介,但鉴于安全性很差,这里不予考虑(代码中一般来说不允许出现全局变量)。
chan通知
func main() {
stop := make(chan bool)
go func() {
for {
select {
case <-stop:
("监控退出,停止了...")
return
default:
("goroutine监控中...")
(2 * )
}
}
}()
(10 * )
("可以了,通知监控停止")
stop <- true
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
(5 * )
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
输出结果:
goroutine监控中...
goroutine监控中...
goroutine监控中...
goroutine监控中...
goroutine监控中...
可以了,通知监控停止
监控退出,停止了...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这种方式还是比较优雅的,但还是有很大的局限性。如果我们需要控制很多的goruntine结束怎么办,难道我们要搞出很多个chan?又或者子goruntine中又衍生出更多的子goruntine怎么办?如果一层层的无穷尽的 goroutine 呢?这就非常复杂了,即使我们定义很多 chan 也很难解决这个问题,因为 goroutine 的关系链就导致了这种场景非常复杂。
初识Context
上面说的这种场景是存在的,比如一个网络请求 Request,每个 Request 都需要开启一个 goroutine 做一些事情,这些 goroutine 又可能会开启其它的 goroutine。所以我们需要一种可以跟踪 goroutine 的方案,才可以达到控制它们的目的,这就是Go语言为我们提供的 Context,称之为上下文非常贴切,它就是 goroutine 的上下文。
利用重写上面示例:
func main() {
ctx, cancel := (())
go func(ctx ) {
for {
select {
case <-():
("监控退出,停止了...")
return
default:
("goroutine监控中...")
(2 * )
}
}
}(ctx)
(10 * )
("可以了,通知监控停止")
cancel()
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
(5 * )
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
Context 控制多个 goroutine
func main() {
ctx, cancel := (())
go watch(ctx, "【监控1】")
go watch(ctx, "【监控2】")
go watch(ctx, "【监控3】")
(10 * )
("可以了,通知监控停止")
cancel()
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
(5 * )
}
func watch(ctx , name string) {
for {
select {
case <-():
(name, "监控退出,停止了...")
return
default:
(name, "goroutine监控中...")
(2 * )
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25