你好,我是四哥。rGu28資訊網——每日最新資訊28at.com
前一篇文章從源碼的角度詳細介紹了 Context 的實現原理,但是還沒有提到 Context 的使用場景,今天我們一起來看下:rGu28資訊網——每日最新資訊28at.com
1.請求鏈路傳值。rGu28資訊網——每日最新資訊28at.com
傳值使用方式如下:rGu28資訊網——每日最新資訊28at.com
func func1(ctx context.Context) {rGu28資訊網——每日最新資訊28at.com
ctx = context.WithValue(ctx, "k1", "v1")rGu28資訊網——每日最新資訊28at.com
func2(ctx)rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
rGu28資訊網——每日最新資訊28at.com
func func2(ctx context.Context) {rGu28資訊網——每日最新資訊28at.com
fmt.Println("func2:",ctx.Value("k1").(string))rGu28資訊網——每日最新資訊28at.com
ctx = context.WithValue(ctx, "k2", "v2")rGu28資訊網——每日最新資訊28at.com
func3(ctx)rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
rGu28資訊網——每日最新資訊28at.com
func func3(ctx context.Context) {rGu28資訊網——每日最新資訊28at.com
fmt.Println("func3:",ctx.Value("k1").(string))rGu28資訊網——每日最新資訊28at.com
fmt.Println("func3:",ctx.Value("k2").(string))rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
rGu28資訊網——每日最新資訊28at.com
func main() {rGu28資訊網——每日最新資訊28at.com
ctx := context.Background()rGu28資訊網——每日最新資訊28at.com
func1(ctx)rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
我們在 func1() 通過函數 WithValue() 設置了一個鍵值對 k1-v1,在 func2() 可以獲取到 func1() 設置的鍵值對,如果調用 func3() 時把這個 ctx 繼續傳入的話,在 func3() 中依然還是可以獲取到 k1-v1。rGu28資訊網——每日最新資訊28at.com
但是在 func1() 中獲取不到 func2() 設置的鍵值對 k2-v2,因為 context 只能自上而下攜帶值,這點需要注意。rGu28資訊網——每日最新資訊28at.com
2.取消耗時操作,及時釋放資源。rGu28資訊網——每日最新資訊28at.com
使用 channel + select 的機制:rGu28資訊網——每日最新資訊28at.com
func func1() error {rGu28資訊網——每日最新資訊28at.com
respC := make(chan int) // 起消息通知作用rGu28資訊網——每日最新資訊28at.com
// 處理邏輯rGu28資訊網——每日最新資訊28at.com
go func() {rGu28資訊網——每日最新資訊28at.com
time.Sleep(time.Second * 3) // 模擬處理業務邏輯rGu28資訊網——每日最新資訊28at.com
respC close(respC)rGu28資訊網——每日最新資訊28at.com
}()rGu28資訊網——每日最新資訊28at.com
rGu28資訊網——每日最新資訊28at.com
// 判斷是否超時rGu28資訊網——每日最新資訊28at.com
select {rGu28資訊網——每日最新資訊28at.com
case r := <-respC:rGu28資訊網——每日最新資訊28at.com
fmt.Printf("Resp: %d ", r)rGu28資訊網——每日最新資訊28at.com
return nilrGu28資訊網——每日最新資訊28at.com
case <-time.After(time.Second * 2): // 超過設置的時間就報錯rGu28資訊網——每日最新資訊28at.com
fmt.Println("catch timeout")rGu28資訊網——每日最新資訊28at.com
return errors.New("timeout")rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
rGu28資訊網——每日最新資訊28at.com
func main() {rGu28資訊網——每日最新資訊28at.com
err := func1()rGu28資訊網——每日最新資訊28at.com
fmt.Printf("func1 error: %v ", err)rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
上面的方式平時也會用到,通過 context 怎么實現呢?rGu28資訊網——每日最新資訊28at.com
下面來看下如何使用 context 進行主動取消、超時取消。rGu28資訊網——每日最新資訊28at.com
主動取消:rGu28資訊網——每日最新資訊28at.com
func func1(ctx context.Context, wg *sync.WaitGroup) error {rGu28資訊網——每日最新資訊28at.com
defer wg.Done()rGu28資訊網——每日最新資訊28at.com
respC := make(chan int)rGu28資訊網——每日最新資訊28at.com
go func() {rGu28資訊網——每日最新資訊28at.com
time.Sleep(time.Second * 5) // 模擬業務邏輯處理rGu28資訊網——每日最新資訊28at.com
respC }()rGu28資訊網——每日最新資訊28at.com
// 取消機制rGu28資訊網——每日最新資訊28at.com
select {rGu28資訊網——每日最新資訊28at.com
case <-ctx.Done():rGu28資訊網——每日最新資訊28at.com
fmt.Println("cancel")rGu28資訊網——每日最新資訊28at.com
return errors.New("cancel")rGu28資訊網——每日最新資訊28at.com
case r := <-respC:rGu28資訊網——每日最新資訊28at.com
fmt.Println(r)rGu28資訊網——每日最新資訊28at.com
return nilrGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
rGu28資訊網——每日最新資訊28at.com
func main() {rGu28資訊網——每日最新資訊28at.com
wg := &sync.WaitGroup{}rGu28資訊網——每日最新資訊28at.com
ctx, cancel := context.WithCancel(context.Background())rGu28資訊網——每日最新資訊28at.com
wg.Add(1)rGu28資訊網——每日最新資訊28at.com
go func1(ctx, wg)rGu28資訊網——每日最新資訊28at.com
time.Sleep(time.Second * 2)rGu28資訊網——每日最新資訊28at.com
cancel() // 主動取消rGu28資訊網——每日最新資訊28at.com
wg.Wait() // 等待 goroutine 退出rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
超時取消:rGu28資訊網——每日最新資訊28at.com
func func1(ctx context.Context) {rGu28資訊網——每日最新資訊28at.com
resp := make(chan int)rGu28資訊網——每日最新資訊28at.com
go func() {rGu28資訊網——每日最新資訊28at.com
time.Sleep(time.Second * 5) // 模擬處理邏輯rGu28資訊網——每日最新資訊28at.com
resp }()rGu28資訊網——每日最新資訊28at.com
// 超時機制rGu28資訊網——每日最新資訊28at.com
select {rGu28資訊網——每日最新資訊28at.com
case <-ctx.Done():rGu28資訊網——每日最新資訊28at.com
fmt.Println("ctx timeout")rGu28資訊網——每日最新資訊28at.com
fmt.Println(ctx.Err())rGu28資訊網——每日最新資訊28at.com
case <-resp:rGu28資訊網——每日最新資訊28at.com
fmt.Println("done")rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
returnrGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
func main() {rGu28資訊網——每日最新資訊28at.com
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)rGu28資訊網——每日最新資訊28at.com
defer cancel()rGu28資訊網——每日最新資訊28at.com
func1(ctx)rGu28資訊網——每日最新資訊28at.com
}3.防止 goroutine 泄露。rGu28資訊網——每日最新資訊28at.com
引自【深度解密 Go 語言之 context[1]】rGu28資訊網——每日最新資訊28at.com
func gen() ch := make(chan int)rGu28資訊網——每日最新資訊28at.com
go func() {rGu28資訊網——每日最新資訊28at.com
var n intrGu28資訊網——每日最新資訊28at.com
for {rGu28資訊網——每日最新資訊28at.com
ch n++rGu28資訊網——每日最新資訊28at.com
time.Sleep(time.Second)rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
}()rGu28資訊網——每日最新資訊28at.com
return chrGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
這是一個可以生成無限整數的協程,但如果我只需要它產生的前 5 個數,那么就會發生 goroutine 泄漏:rGu28資訊網——每日最新資訊28at.com
func main() {rGu28資訊網——每日最新資訊28at.com
for n := range gen() {rGu28資訊網——每日最新資訊28at.com
fmt.Println(n)rGu28資訊網——每日最新資訊28at.com
if n == 5 {rGu28資訊網——每日最新資訊28at.com
breakrGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
// ……rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
當 n == 5 的時候,直接 break 掉。那么 gen 函數的協程就會執行無限循環,永遠不會停下來。發生了 goroutine 泄漏。rGu28資訊網——每日最新資訊28at.com
用 context 改進這個例子:rGu28資訊網——每日最新資訊28at.com
func gen(ctx context.Context) ch := make(chan int)rGu28資訊網——每日最新資訊28at.com
go func() {rGu28資訊網——每日最新資訊28at.com
var n intrGu28資訊網——每日最新資訊28at.com
for {rGu28資訊網——每日最新資訊28at.com
select {rGu28資訊網——每日最新資訊28at.com
case <-ctx.Done():rGu28資訊網——每日最新資訊28at.com
returnrGu28資訊網——每日最新資訊28at.com
case ch n++rGu28資訊網——每日最新資訊28at.com
time.Sleep(time.Second)rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
}()rGu28資訊網——每日最新資訊28at.com
return chrGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
rGu28資訊網——每日最新資訊28at.com
func main() {rGu28資訊網——每日最新資訊28at.com
ctx, cancel := context.WithCancel(context.Background())rGu28資訊網——每日最新資訊28at.com
defer cancel() // 避免其他地方忘記 cancel,且重復調用不影響rGu28資訊網——每日最新資訊28at.com
rGu28資訊網——每日最新資訊28at.com
for n := range gen(ctx) {rGu28資訊網——每日最新資訊28at.com
fmt.Println(n)rGu28資訊網——每日最新資訊28at.com
if n == 5 {rGu28資訊網——每日最新資訊28at.com
cancel()rGu28資訊網——每日最新資訊28at.com
breakrGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
// ……rGu28資訊網——每日最新資訊28at.com
}rGu28資訊網——每日最新資訊28at.com
增加一個 context,在 break 前調用 cancel 函數,取消 goroutine。gen 函數在接收到取消信號后,直接退出,系統回收資源。rGu28資訊網——每日最新資訊28at.com
總結rGu28資訊網——每日最新資訊28at.com
這篇文章列出的幾個例子是 context 最基本的使用場景,其他框架、第三包基本上都是從這幾種用法擴展的,所以非常有必要掌握基礎用法。rGu28資訊網——每日最新資訊28at.com
另外希望這篇文章能給你帶來幫助,如果文中有理解錯誤之處或者你還想到其他用法,可以在留言區留言,一定回復!抱團學習不孤單!rGu28資訊網——每日最新資訊28at.com
參考資料rGu28資訊網——每日最新資訊28at.com
[1]深度解密Go語言之context: https://qcrao.com/2019/06/12/dive-into-go-context/rGu28資訊網——每日最新資訊28at.com
本文鏈接:http://www.www897cc.com/showinfo-119-2167-0.html細數 Context 使用場景
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 云計算開發:Python3-isdecimal()方法詳解
下一篇: 數據分析八大模型:漏斗模型