Go的上下文應(yīng)用
1.背景原理
Go上下文就是context。對于go來說go routine(協(xié)程)是一個非常有用的方法。
本來用chan(通道)也可以達到控制的目的,無奈如果開的協(xié)程太多的話,控制起來就很麻煩。光命名都頭疼。
后來又有了sync.WaitGroup來控制,不過好像也沒有那么優(yōu)雅。沒有怎么研究。
context(上下文)的出現(xiàn),優(yōu)雅的解決了這個問題。
context的原理:

clipboard.png

2.png
context可以創(chuàng)建父子結(jié)構(gòu),樹形結(jié)構(gòu),如上圖。
2.舉例說明
設(shè)定一個需求:
需要建立一組協(xié)程,協(xié)程圖如下:
main-->A1(協(xié)程)-->A1_1(協(xié)程)->A1_1_1(協(xié)程)
|->B(協(xié)程)->B1(協(xié)程)
A1熔斷時間:5s; A_1熔斷時間:4s; A_1_1熔斷時間:3s
B1錯誤,全部結(jié)束
上源碼:
package main
import (
"context"
"fmt"
"log"
"time"
)
func main() {
contextNodeMain()
}
/*
需求概述
主程序的協(xié)程
main->A->A_1->A_1_1
|->B->B1
主程序的斷熔時間為5s;A為4s;A_1為3.5s;A_1_1為3S;
若B1錯誤,主程序強制結(jié)束
*/
func contextNodeMain() {
d := time.Now().Add(5000 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)
defer func() { cancel() }()
//B1結(jié)束通道
ch := make(chan bool)
go A(ctx)
go B(ctx, ch)
select {
case <-ctx.Done(): //全部聲明周期燃盡
log.Println("所有協(xié)程關(guān)閉")
case <-ch: //B1發(fā)生錯誤全部結(jié)束
log.Println("發(fā)生錯誤,其余協(xié)程結(jié)束")
return
//cancel()
}
}
func B(ctx context.Context, ch chan bool) {
log.Println("B 開始工作")
B_1(ch)
}
func B_1(ch chan bool) {
time.Sleep(time.Second * 10)
log.Println("B_1 發(fā)生錯誤")
ch <- false
}
func A(ctx context.Context) {
ctx_A, cancel := context.WithTimeout(ctx, time.Millisecond*time.Duration(4000)) //A_1執(zhí)行時間為3.5秒
log.Println("A 開始工作")
defer cancel()
go A_1(ctx_A)
for {
select {
case <-ctx.Done():
log.Println("A停止工作")
return
// case <-time.After(time.Second):
// log.Println("A wroking")
}
}
}
func A_1(ctx context.Context) {
ctx_A_1, cancel := context.WithTimeout(ctx, time.Millisecond*time.Duration(3000)) //A_1_1的執(zhí)行時間為3秒
log.Println("A_1 開始工作")
defer cancel()
go A_1_1(ctx_A_1)
for {
select {
case <-ctx.Done():
log.Println("A_1停止工作")
return
}
}
}
func A_1_1(ctx context.Context) {
log.Println("A_1_1 開始工作")
for {
select {
case <-ctx.Done():
log.Println("A_1_1停止工作")
return
}
}
}

QQ截圖20191106100353.png
從結(jié)果中我們可以看到
1.10s時,A和B都開始工作。
2.在13秒的時候,A_1_1停止工作,正好是3S。
3.在14s的時候,A_1停止工作,差4s
4.在15S的時候,協(xié)程關(guān)閉,差5s
也就是按照我們設(shè)想的,停止順序為A_1_1->A_1->A
如果B_1發(fā)生錯誤,我們只需要改一下B,模擬B_1發(fā)生錯誤修改代碼如下
func B_1(ch chan bool) {
time.Sleep(time.Second * 3)
log.Println("B_1 發(fā)生錯誤")
ch <- false
}
結(jié)果如下:

QQ截圖20191106101013.png
如果B_1發(fā)生錯誤,協(xié)程全部關(guān)閉。