所謂的context,直譯過來是上下文的意思。
在之前的學(xué)習(xí)中,碰到上下文的概念是在JVM運行時數(shù)據(jù)區(qū)的程序計數(shù)器中,代表線程切換時保存的數(shù)據(jù),用以在線程切換回來時繼續(xù)從之前的位置執(zhí)行。
go中的context,要從三個方面說起:
- 什么是context
- context的應(yīng)用場景
- context源碼解讀
1.什么是context?
context是用來控制goroutine的一種方式:在復(fù)雜goroutine應(yīng)用場景中,往往需要在api邊界和過程之間傳遞截止時間、取消信號或者其他相關(guān)的數(shù)據(jù)
goroutine是輕量級線程,用于實現(xiàn)go的并發(fā)編程
2.context的應(yīng)用場景
goroutine的使用方式一般有三種
- WaitGroup
- Channel
- Context
WaitGroup的使用方式:先往waitgroup中添加job數(shù)量,之后在不同的goroutine執(zhí)行wg.Done(),在主groutine中執(zhí)行wg.Wait() 等待
func CtxWaitGroup() {
var wg sync.WaitGroup
wg.Add(2) //在waitgroup中添加job數(shù)量
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跑太久了,我們需要發(fā)送一個信息讓他停止下來,這種情況下可以使用Channel+Select的模式
// 如何主動通知停止
func CtxStopInitiative() {
stop := make(chan bool) // 定義一個channel,傳遞true/false
go func() { // 創(chuàng)建一個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發(fā)送一個停止請求
time.Sleep(5 * time.Second)
}
Context的使用方式:上面兩種情況應(yīng)對的是單層的goroutine調(diào)度,如果是goroutine又創(chuàng)建了goroutine,類似一個項目分給幾個組,幾個組又分別安排給組里不同的成員完成。如果我們需要某個goroutine完成時,它的子goroutine也已經(jīng)完成,可以通過Context實現(xiàn)。
func CtxContextManyGoroutine() {
// 父goroutine其實創(chuàng)建了三個子goroutine:worker;
// 而每個worker又創(chuàng)建了自己的goroutine;
// 仍然在父goroutine創(chuàng)建一個context對象
// 并將其通過函數(shù)參數(shù),分發(fā)給所有worker,當(dāng)父goroutine需要停止時
// 調(diào)用cancel()函數(shù),所有子goroutine會接收到<-ctx.Done()結(jié)束消息,作出相應(yīng)處理
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("建立財務(wù)共享中心,老財全部優(yōu)化!")
cancel() // ctx發(fā)出了結(jié)束信號,代表主goroutine即將結(jié)束
time.Sleep(1 * time.Second)
fmt.Println("老財們都滾蛋了!")
}
func worker(ctx context.Context, str string) {
go func() {
for {
select {
case <-ctx.Done(): // worker-goroutine接收到結(jié)束信號,打印消息后直接返回結(jié)束
fmt.Println(str, " 你被優(yōu)化了!")
return
default:
fmt.Println(str, " 工作中")
time.Sleep(1 * time.Second)
}
}
}()
}