Golang-Context扫盲与原理解析
一.什么是Context?
- context是一个包,是Go1.7引入的标注库,中文译做上下文,准确的说是goroutine的上下文,包含goroutine的运行状态,环境,现场等信息。
- context主要用于在goroutine之间传递上下文信息,比如取消信号,超时时间,截止时间,kv等。
二.为什么要有Context?
在Go中,控制并发有两种经典的方式,一个是WaitGroup,另外一个就是context
- WaitGroup:控制多个groutine同时完成,这是等待的方式,等那些必要的goroutine都工作完了我才能工作
- Context:主动通知某一个groutine结束,这是主动通知的方式,通知某些groutine你不要再工作了
其实主动通知的方式,除了context,还有一种方式也可以实现
- channle + select
func main() {
stop := make(chan bool)
go func() {
for {
select {
case <-stop:
fmt.Println("监控退出,停止了...")
return
default:
fmt.Println("goroutine监控中...")
time.Sleep(2 * time.Second)
}
}
}()
time.Sleep(10 * time.Second)
fmt.Println("可以了,通知监控停止")
stop<- true
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
time.Sleep(5 * time.Second)
}
采用channle + select 这种方式来实现主动通知,有两个致命的缺点:
- 只能通知一个groutine结束,无法应对很多goroutine都需要结束的情况
- 无法应对goroutine又衍生出其他更多的goroutine的情况
上述这两种场景其实在业务中非常的常见
- 场景1:比如一个网络请求Request,每个Request都需要开启一个goroutine做一些事情,这些goroutine又可能会开启其他的goroutine,具体表现在Go的Server中,通常每一个请求都会启动若干个goroutine同时工作,有些去数据库拿数据,有些调用下游接口获取相关数据,这些goroutine需要共享这个请求的基本数据,例如登录token,处理请求的最大超时时间等等,当请求被取消或是处理时间太长,这时,所有正在为这个请求工作的goroutine都需要快速退出,因为他们的工作成果不再被需要了
为应对上述场景,并且使得goroutine是可追踪的,context应运而生
三.Context 如何使用?
1.context控制多个goroutine
func main() {
ctx, cancel := context.WithCancel(context.Background())
go watch(ctx,"【监控1】")
go watch(ctx,"【监控2】")
go watch(ctx,"【监控3】")
time.Sleep(10 * time.Second)
fmt.Println("可以了,通知监控停止")
cancel()
//为了检测监控过是否停止,如果没有监控输出,就表示停止了
time.Sleep(5 * time.Second)
}
func watch(ctx context.Context, name string) {
for {
select {
case <-ctx.Done():
fmt.Println(name,"监控退出,停止了...")
return
default:
fmt.Println(name,"goroutine监控中...")
time.Sleep(2 * time.Second)
}
}
}
上述样例,context控制了三个goroutine,当context cancle之后,这三个goroutine便都退出了
2.传递共享数据
packag