go語言中context的使用說明
概述
Context 是 Go 語言中非常重要的一個概念,它主要用于跨多個函數(shù)或 goroutine 傳遞 取消信號、超時控制、截止時間 和 請求范圍數(shù)據(jù)。
在并發(fā)編程中,Context 提供了更好的控制和管理,尤其是當你需要在多個 goroutine 之間傳遞狀態(tài)或進行資源清理時。
主要功能
Context 主要有以下幾個功能:
- 取消信號:通知一個或多個 goroutine 取消它們正在執(zhí)行的工作。
- 超時和截止時間:指定操作的最大執(zhí)行時間,防止阻塞操作過長時間。
- 傳遞請求范圍數(shù)據(jù):攜帶請求范圍內(nèi)的數(shù)據(jù),通常用于請求 ID、用戶信息等。
Context 的三種基本類型
Go 中的 context 包提供了幾種常用的 Context 類型:
context.Background():通常作為根Context,表示沒有附加數(shù)據(jù)或取消信號的上下文。它通常是根上下文,作為其他上下文的父上下文。context.TODO():表示你暫時沒有確定使用什么樣的Context,通常用于占位。context.WithCancel(parent):創(chuàng)建一個可取消的Context,并返回一個取消函數(shù),當你調(diào)用這個函數(shù)時,Context會被取消。context.WithTimeout(parent, timeout):創(chuàng)建一個帶有超時的Context,指定最大等待時間,超過這個時間會自動取消。context.WithDeadline(parent, deadline):指定一個具體的截止時間,超過這個時間后自動取消。context.WithValue(parent, key, value):創(chuàng)建一個攜帶鍵值對數(shù)據(jù)的Context,通常用于傳遞請求級別的數(shù)據(jù)(例如,用戶身份信息)。
常見用法舉例
context.WithCancel傳遞取消信號
主要場景:
- 手動控制并發(fā)任務的終止。
- 優(yōu)雅退出:在一個任務中途需要取消時,用
cancel()通知所有相關(guān)的 goroutine 停止執(zhí)行。
代碼示例:
package main
import (
"context"
"fmt"
"time"
)
func main() {
// 創(chuàng)建一個可取消的 Context
ctx, cancel := context.WithCancel(context.Background())
// 啟動一個 goroutine,監(jiān)聽取消信號
go func(ctx context.Context) {
for {
select {
case <-ctx.Done(): // 檢測到取消信號
fmt.Println("Goroutine stopped")
return
default:
// 模擬工作
fmt.Println("Working...")
time.Sleep(1 * time.Second)
}
}
}(ctx)
// 主線程等待 3 秒后取消
time.Sleep(3 * time.Second)
cancel() // 發(fā)送取消信號
// 等待 goroutine 退出
time.Sleep(1 * time.Second)
fmt.Println("Main program exited")
}解釋:
- 主線程創(chuàng)建了一個帶有取消功能的上下文
ctx。 - 子 goroutine 使用
ctx.Done()監(jiān)聽取消信號。 - 主線程 3 秒后調(diào)用
cancel(),子 goroutine 檢測到信號后優(yōu)雅退出。
使用 WithTimeout 設(shè)置超時
context.WithTimeout 用于設(shè)置一個超時時間,超過該時間后 Context 會自動取消,適用于需要限時執(zhí)行的操作。防止某些任務阻塞的時間過長。
package main
import (
"context"
"fmt"
"time"
)
func main() {
// 設(shè)置超時時間為 2 秒
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // 確保超時后取消 ctx
// 啟動一個模擬長時間執(zhí)行的任務
go longRunningTask(ctx)
// 等待超時或任務完成
<-ctx.Done()
if ctx.Err() == context.DeadlineExceeded {
fmt.Println("Timeout reached")
}
}
func longRunningTask(ctx context.Context) {
select {
case <-time.After(3 * time.Second): // 模擬長時間任務
fmt.Println("Task completed")
case <-ctx.Done():
// 任務被取消或超時
fmt.Println("Task cancelled due to timeout")
}
}WithDeadline的用法和Withtimeout用法類似,只是一個傳入的參數(shù)是等待時間,一個傳入的參數(shù)是截止時間。
使用 WithValue 傳遞數(shù)據(jù)
context.WithValue 可以在 Context 中存儲鍵值對,通常用于傳遞請求級別的數(shù)據(jù)(例如用戶身份、請求 ID 等)。
package main
import (
"context"
"fmt"
)
func main() {
// 創(chuàng)建一個上下文并傳遞數(shù)據(jù)
ctx := context.WithValue(context.Background(), "userID", 12345)
// 將 ctx 傳遞給其他函數(shù)
processRequest(ctx)
}
func processRequest(ctx context.Context) {
// 從 ctx 中提取數(shù)據(jù)
userID := ctx.Value("userID")
if userID != nil {
fmt.Println("User ID:", userID)
} else {
fmt.Println("No user ID found")
}
}使用 WithValue 小心:context.WithValue 并不是用于傳遞大量數(shù)據(jù)的,主要用于傳遞少量的上下文信息,比如請求 ID 等。
如果傳遞過多的數(shù)據(jù),會使得 Context 難以維護。
常用的相關(guān)方法和常量
ctx.Done():返回一個 channel,當Context被取消時該 channel 會被關(guān)閉。ctx.Err():返回Context被取消的錯誤,通常是context.Canceled或context.DeadlineExceeded。ctx.Value(key):獲取在Context中傳遞的數(shù)據(jù)。
context如何控制goroutine的執(zhí)行
從上面的舉例可以看出,在每個goroutine中通過判斷ctx.Done()是否被執(zhí)行,從而知道任務是否被取消/超時/到達截止日期。
當 Context 被取消(調(diào)用 cancel())或超時/到達截止時間時,ctx.Done() 所關(guān)聯(lián)的 channel 會關(guān)閉,此時select語句就可以執(zhí)行ctx.Done()對應的分支。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Golang設(shè)計模式工廠模式實戰(zhàn)寫法示例詳解
這篇文章主要為大家介紹了Golang 工廠模式實戰(zhàn)寫法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08
GO中?分組聲明與array,?slice,?map函數(shù)
這篇文章主要介紹了GO中?分組聲明與array,slice,map函數(shù),Go語言中,同時聲明多個常量、變量,或者導入多個包時,可采用分組的方式進行聲明,下面詳細介紹需要的小伙伴可以參考一下2022-03-03
Go語言同步與異步執(zhí)行多個任務封裝詳解(Runner和RunnerAsync)
這篇文章主要給大家介紹了關(guān)于Go語言同步與異步執(zhí)行多個任務封裝(Runner和RunnerAsync)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。2018-01-01
Golang實現(xiàn)CronJob(定時任務)的方法詳解
這篇文章主要為大家詳細介紹了Golang如何通過一個單 pod 去實現(xiàn)一個常駐服務,去跑定時任務(CronJob),文中的示例代碼講解詳細,需要的可以參考下2023-04-04

