所谓的context,直译过来是上下文的意思。
在之前的学习中,碰到上下文的概念是在JVM运行时数据区的程序计数器中,代表线程切换时保存的数据,用以在线程切换回来时继续从之前的位置执行。
go中的context,要从三个方面说起:
- 什么是context
- context的应用场景
- context源码解读
1.什么是context?
context是用来控制goroutine的一种方式:在复杂goroutine应用场景中,往往需要在api边界和过程之间传递截止时间、取消信号或者其他相关的数据
goroutine是轻量级线程,用于实现go的并发编程
2.context的应用场景
goroutine的使用方式一般有三种
- WaitGroup
- Channel
- Context
WaitGroup的使用方式:先往waitgroup中添加job数量,之后在不同的goroutine执行wg.Done()
,在主groutine中执行wg.Wait()
等待
func CtxWaitGroup() {
var wg sync.WaitGroup
wg.Add(2) //在waitgroup中添加job数量
go func() {
time.Sleep(2 * time.Second)
fmt.Println("老财做账")
wg.Done() // 通知waitgroup本job完成
}()
go func() {
time.Sleep(1 * time.Second)
fmt.Println("老财审单")
wg.Done()
}()
wg.Wait() //等待waitgroup中的job完成
fmt.Println("这就是老财们的日常工作")
}
Channel的使用方式:配合select使用,希望能主动停止某个goroutine,比如某个goroutine跑太久了,我们需要发送一个信息让他停止下来,这种情况下可以使用Channel+Select的模式
// 如何主动通知停止
func CtxStopInitiative() {
stop := make(chan bool) // 定义一个channel,传递true/false
go func() { // 创建一个goroutine
for {
select {
case <-stop: // 如果channel接收到停止请求
fmt.Println("You are fired!")
return
default: // 未接收到停止请求前
fmt.Println("老财工作中")
time.Sleep(1 * time.Second)
}
}
}()
time.Sleep(5 * time.Second) // 等待五秒
fmt.Println("那个老财动作太慢了!开除!")
stop <- true // 等不下去了,向channel发送一个停止请求
time.Sleep(5 * time.Second)
}
Context的使用方式:上面两种情况应对的是单层的goroutine调度,如果是goroutine又创建了goroutine,类似一个项目分给几个组,几个组又分别安排给组里不同的成员完成。如果我们需要某个goroutine完成时,它的子goroutine也已经完成,可以通过Context实现。
func CtxContextManyGoroutine() {
// 父goroutine其实创建了三个子goroutine:worker;
// 而每个worker又创建了自己的goroutine;
// 仍然在父goroutine创建一个context对象
// 并将其通过函数参数,分发给所有worker,当父goroutine需要停止时
// 调用cancel()函数,所有子goroutine会接收到<-ctx.Done()结束消息,作出相应处理
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx, "老财1")
go worker(ctx, "老财2")
go worker(ctx, "老财3")
time.Sleep(1 * time.Second) // 主goroutine阻塞1秒,观察三个worker-goroutine运行情况
fmt.Println("建立财务共享中心,老财全部优化!")
cancel() // ctx发出了结束信号,代表主goroutine即将结束
time.Sleep(1 * time.Second)
fmt.Println("老财们都滚蛋了!")
}
func worker(ctx context.Context, str string) {
go func() {
for {
select {
case <-ctx.Done(): // worker-goroutine接收到结束信号,打印消息后直接返回结束
fmt.Println(str, " 你被优化了!")
return
default:
fmt.Println(str, " 工作中")
time.Sleep(1 * time.Second)
}
}
}()
}
网友评论