Go語言中的上下文(Context)是一種用於在 Goroutines 之間傳遞取消信號、截止時間和其他請求範圍值的標準方式。context 包提供了 Context 類型和一些相關的函數,用於在併發程式中有效地傳遞上下文信息。 在Go語言中,上下文通常用於以下場景: 請求的傳遞:當一個請求從客戶端 ...
Go語言中的上下文(Context)是一種用於在 Goroutines 之間傳遞取消信號、截止時間和其他請求範圍值的標準方式。context
包提供了 Context
類型和一些相關的函數,用於在併發程式中有效地傳遞上下文信息。
在Go語言中,上下文通常用於以下場景:
- 請求的傳遞:當一個請求從客戶端發送到伺服器時,可以使用上下文來攜帶與該請求相關的數據。這些數據可以是用戶的身份信息、請求的元數據或其他與請求相關的信息。通過將上下文傳遞給處理該請求的goroutine,可以確保在整個處理過程中訪問這些數據。
- 取消操作:上下文可以用於取消正在進行的操作。當用戶或其他代碼發送取消信號時,可以將該信號傳遞給正在執行操作的goroutine。goroutine在接收到取消信號後,可以根據需要執行清理操作並退出。
- 截止時間:有時候需要在一段時間後終止正在進行的操作。通過將截止時間與上下文一起傳遞給goroutine,可以確保在超過截止時間後執行適當的清理操作並退出。
- 跨多個服務通信:當在分散式系統中使用Go語言時,上下文可以用於跨不同的服務之間傳遞請求數據、取消信號和截止時間。通過使用上下文,可以確保在整個系統中的各個服務之間保持一致的上下文和請求生命周期管理。
通過使用上下文,可以有效地在 Goroutines 之間傳遞取消信號、截止時間和請求範圍的值,從而更好地控制併發程式的行為。
1. context.Context
介面
Context
介面定義了在 Goroutines 之間傳遞的上下文的基本方法:
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
- Deadline():返回上下文的截止時間。如果存在截止時間,
ok
為true
,否則為false
。 - Done():返回一個通道,該通道關閉時表示上下文被取消或者超過了截止時間。
- Err():返回上下文取消的原因。如果上下文沒有被取消,則返回
nil
。 - Value(key):返回與給定
key
關聯的值。這允許在上下文中傳遞請求範圍的數據。
2. 創建上下文
在 Go 中,上下文可以通過 context.Background()
創建,它是一個無值的上下文,通常用作根上下文。根上下文不能被取消,也不能傳遞截止時間。
ctx := context.Background()
可以使用 context.WithCancel
、context.WithTimeout
、context.WithDeadline
和 context.WithValue
等函數創建派生上下文,這些函數分別用於創建帶有取消、超時、截止時間和值的上下文。
// 創建一個帶有取消功能的上下文
ctx, cancel := context.WithCancel(context.Background())
// 創建一個帶有超時的上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
// 創建一個帶有截止時間的上下文
deadline := time.Now().Add(2 * time.Second)
ctx, cancel := context.WithDeadline(context.Background(), deadline)
// 創建一個帶有值的上下文
key := "key"
value := "value"
ctx := context.WithValue(context.Background(), key, value)
3. 傳遞上下文
在 Go 中,通過函數參數將上下文傳遞給調用的函數,從而使調用的函數能夠感知上下文的取消或超時。例如:
func myFunction(ctx context.Context) {
// 在這裡使用 ctx 處理邏輯
select {
case <-ctx.Done():
// 上下文被取消,執行清理工作
fmt.Println("Context canceled")
return
default:
// 繼續正常的邏輯
fmt.Println("Doing some work")
}
}
func main() {
// 創建帶有取消功能的上下文
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 啟動 Goroutine,傳遞上下文
go myFunction(ctx)
// 主 Goroutine 執行一些工作
time.Sleep(2 * time.Second)
}
4. 上下文的取消
調用 cancel()
函數會取消與上下文相關的 Goroutines。一旦上下文被取消,與之關聯的所有 Goroutines 都會收到取消信號。
ctx, cancel := context.WithCancel(context.Background())
// 啟動 Goroutine,傳遞上下文
go func(ctx context.Context) {
select {
case <-ctx.Done():
// 上下文被取消,執行清理工作
fmt.Println("Context canceled")
return
}
}(ctx)
// 取消上下文
cancel()
5. 上下文的超時和截止時間
使用 context.WithTimeout
或 context.WithDeadline
函數可以設置上下文的超時或截止時間。當超過指定的時間後,上下文會自動取消。
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
// 啟動 Goroutine,傳遞上下文
go func(ctx context.Context) {
select {
case <-ctx.Done():
// 上下文超時,執行清理工作
fmt.Println("Context timeout")
return
}
}(ctx)
// 主 Goroutine 執行一些工作
time.Sleep(3 * time.Second)
6. 上下文值
context.WithValue
函數可以用於在上下文中傳遞請求範圍的值。這些值可以通過 context.Value
方法在上下文中檢索。
ctx := context.WithValue(context.Background(), "user", "john_doe")
// 從上下文中獲取值
value := ctx.Value("user")
fmt.Println(value) // 輸出: john_doe
7. 上下文的鏈式調用
可以通過鏈式調用的方式,將多個上下文進行組合,形成一個父子關係的上下文鏈。
parentCtx := context.Background()
ctx1, cancel1 := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel1()
ctx2, cancel2 := context.WithCancel(ctx1)
defer cancel2()
上述的 ctx2
是 ctx1
的子上下文,當 ctx1
超時或被取消時,ctx2
也會相應地被取消。
聲明:本作品採用署名-非商業性使用-相同方式共用 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請註明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意