一文讓你理解go語言的Context
Context上下文
Context的出現(xiàn)是為了解決在大型應(yīng)用程序中的并發(fā)環(huán)境下,協(xié)調(diào)和管理多個(gè)goroutine之間的通信、超時(shí)和取消操作的問題。它提供了一種標(biāo)準(zhǔn)化的機(jī)制,以便在程序的不同部分之間傳遞請求相關(guān)的值,并且可以在整個(gè)調(diào)用鏈中傳播和取消這些值。
Context的主要目的是:
- 傳遞請求范圍的值:通過Context,可以在不同的函數(shù)調(diào)用中傳遞請求相關(guān)的值,例如請求ID、用戶認(rèn)證信息、日志記錄器等。這樣可以避免在函數(shù)之間顯式傳遞這些值,使代碼更加清晰簡潔。
- 控制并發(fā)操作:通過Context的取消信號(hào),可以通知相關(guān)的goroutine停止運(yùn)行并返回,從而實(shí)現(xiàn)對并發(fā)操作的控制。這對于處理長時(shí)間運(yùn)行的操作或避免資源泄漏非常有用。
- 設(shè)置截止時(shí)間:Context允許為操作設(shè)置截止時(shí)間,超過該時(shí)間則自動(dòng)取消相關(guān)的操作。這對于避免長時(shí)間等待或超時(shí)的情況非常有用,可以提高系統(tǒng)的可靠性和性能。
- 傳播和繼承:通過Context,可以在整個(gè)調(diào)用鏈中傳播和繼承請求范圍的值和取消信號(hào),而無需顯式地傳遞它們。這樣可以減少代碼中的重復(fù)操作,并確保所有相關(guān)的goroutine都能收到相同的上下文信息。
總而言之,Context的出現(xiàn)是為了提供一種統(tǒng)一的機(jī)制,用于管理并發(fā)操作、傳遞請求相關(guān)的值和控制操作的超時(shí)和取消。它簡化了并發(fā)編程模型,提高了代碼的可讀性、可維護(hù)性和可測試性。
1. Context接口定義
?type Context interface { ? ?Deadline() (deadline time.Time, ok bool) ? ?Done <-chan struct{} ? ?Err() error ? ?Value(key interface{}) interface{} ?}
Deadline() (deadline time.Time, ok bool)
:方法功能: Deadline方法返回代表此上下文中的工作應(yīng)該被取消的時(shí)間。當(dāng)沒有設(shè)置截止時(shí)間時(shí),Deadline方法返回ok==false。連續(xù)調(diào)用Deadline方法將返回相同的結(jié)果:
deadline
:表示Context的截止時(shí)間,如果沒有設(shè)置截止時(shí)間或截止時(shí)間已過,則返回零值Deadline方法返回ok==false。ok
:布爾值,指示截止時(shí)間是否存在。如果存在截止時(shí)間,則為true
;否則為false
。
Done() <-chan struct{}
:方法功能:返回一個(gè)只讀的通道(
<-chan struct{}
),該通道在Context被取消或過期時(shí)關(guān)閉。返回值:返回一個(gè)只讀的通道,可以通過該通道接收到Context的取消或過期信號(hào)。
Err() error
:方法功能:返回Context被取消的原因(錯(cuò)誤信息)。
返回值:
- 如果Context已被取消,返回一個(gè)非空的錯(cuò)誤對象,描述取消的原因。
- 如果Context尚未被取消,或者取消原因未知,則返回
nil
。
Value(key interface{}) interface{}
:方法功能:根據(jù)給定的鍵(key),返回與Context相關(guān)聯(lián)的值。
參數(shù):
key
表示要檢索的值的鍵。返回值:
- 如果Context關(guān)聯(lián)的值存在,則返回與給定鍵關(guān)聯(lián)的值(類型為
interface{}
)。 - 如果不存在與給定鍵關(guān)聯(lián)的值,則返回
nil
。
- 如果Context關(guān)聯(lián)的值存在,則返回與給定鍵關(guān)聯(lián)的值(類型為
2. 錯(cuò)誤設(shè)置
?// Canceled is the error returned by Context.Err when the context is canceled. ?var Canceled = errors.New("context canceled") ?? ?// DeadlineExceeded is the error returned by Context.Err when the context's ?// deadline passes. ?var DeadlineExceeded error = deadlineExceededError{} ?? ?type deadlineExceededError struct{} ?? ?func (deadlineExceededError) Error() string ? { return "context deadline exceeded" } ?func (deadlineExceededError) Timeout() bool ? { return true } ?func (deadlineExceededError) Temporary() bool { return true } ??
在上述代碼中,定義了兩個(gè)預(yù)定義的錯(cuò)誤變量:Canceled和DeadlineExceeded,用于表示上下文取消和超時(shí)。
Canceled
是在上下文被取消時(shí)由Context.Err
返回的錯(cuò)誤。它是一個(gè)errors.New
創(chuàng)建的錯(cuò)誤,表示上下文已被取消。
DeadlineExceeded
是在上下文的截止時(shí)間過去時(shí)由Context.Err
返回的錯(cuò)誤。它是一個(gè)自定義的錯(cuò)誤類型deadlineExceededError
的實(shí)例。該類型實(shí)現(xiàn)了error
接口的方法。
Error()
方法返回表示上下文截止時(shí)間已過的錯(cuò)誤字符串,即"context deadline exceeded"。Timeout()
方法返回true
,指示該錯(cuò)誤是由于超時(shí)引起的。Temporary()
方法返回true
,指示該錯(cuò)誤是臨時(shí)性的。
3. emptyCtx
?type emptyCtx int ?? ?func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { ? return ?} ?? ?func (*emptyCtx) Done() <-chan struct{} { ? return nil ?} ?? ?func (*emptyCtx) Err() error { ? return nil ?} ?? ?func (*emptyCtx) Value(key any) any { ? return nil ?} ?? ?func (e *emptyCtx) String() string { ? switch e { ? case background: ? return "context.Background" ? case todo: ? return "context.TODO" ? } ? return "unknown empty Context" ?}
emptyCtx
是一個(gè)空的上下文類型。它具有以下特點(diǎn):
- 永遠(yuǎn)不會(huì)被取消:
emptyCtx
是一個(gè)不可取消的上下文,即沒有任何操作會(huì)導(dǎo)致它被取消。 - 沒有值:
emptyCtx
不存儲(chǔ)任何與其關(guān)聯(lián)的值。 - 沒有截止時(shí)間:
emptyCtx
沒有設(shè)置任何截止時(shí)間,即沒有截止時(shí)間的限制。
emptyCtx
的類型不是struct{}
,因?yàn)檫@樣的變量必須具有不同的地址。實(shí)際上,emptyCtx
是一個(gè)內(nèi)部類型,用于表示一個(gè)特殊的空上下文。
該空上下文在一些特殊情況下使用,例如作為根上下文或默認(rèn)上下文,在沒有顯式提供上下文的情況下,可以使用它來代替。由于其不可取消、沒有值和截止時(shí)間的特性,它在某些場景下提供了一個(gè)空的占位符上下文實(shí)例。
4. 兩個(gè)emptyCtx
?? ?var ( ? background = new(emptyCtx) ? todo ? ? ? = new(emptyCtx) ?) ?? ?func Background() Context { ? return background ?} ?? ?func TODO() Context { ? return todo ?} ??
4.1. Background
Background
函數(shù)返回一個(gè)非空的空上下文。它具有以下特點(diǎn):
- 永遠(yuǎn)不會(huì)被取消:
Background
上下文是一個(gè)不可取消的上下文,即沒有任何操作會(huì)導(dǎo)致它被取消。 - 沒有值:
Background
上下文不存儲(chǔ)任何與其關(guān)聯(lián)的值。 - 沒有截止時(shí)間:
Background
上下文沒有設(shè)置任何截止時(shí)間,即沒有截止時(shí)間的限制。
Background
上下文是一個(gè)常用的上下文實(shí)例,通常用于主函數(shù)、初始化過程、測試以及作為傳入請求的頂級上下文。它提供了一個(gè)空的占位符上下文,適用于不需要具體上下文的場景。由于其不可取消、沒有值和截止時(shí)間的特性,Background
上下文在啟動(dòng)程序、進(jìn)行初始化操作以及處理入站請求時(shí)經(jīng)常被使用。
4.2. TODO
TODO
函數(shù)返回一個(gè)非空的空上下文。在代碼中,當(dāng)不清楚使用哪個(gè)上下文或者上下文尚不可用(因?yàn)橹車暮瘮?shù)尚未被擴(kuò)展為接受上下文參數(shù))時(shí),應(yīng)使用context.TODO
。
TODO
上下文是一個(gè)臨時(shí)的占位符上下文,用于表示上下文尚未確定或不可用的情況。它可以作為編碼過程中的一種暫時(shí)解決方案,在代碼進(jìn)一步完善之前使用。當(dāng)需要傳遞上下文但還沒有明確選擇使用哪個(gè)上下文時(shí),可以使用TODO
來占位,以后根據(jù)具體情況進(jìn)行替換。
5. CancleFun
?// A CancelFunc tells an operation to abandon its work. ?// A CancelFunc does not wait for the work to stop. ?// A CancelFunc may be called by multiple goroutines simultaneously. ?// After the first call, subsequent calls to a CancelFunc do nothing. ?type CancelFunc func()
CancelFunc
是一個(gè)函數(shù)類型,用于通知某個(gè)操作放棄其工作。CancelFunc
不會(huì)等待工作停止,它僅僅是發(fā)送一個(gè)取消信號(hào)。CancelFunc
可以被多個(gè) goroutine 同時(shí)調(diào)用。第一次調(diào)用之后,對 CancelFunc
的后續(xù)調(diào)用不會(huì)產(chǎn)生任何效果。
當(dāng)需要取消某個(gè)操作時(shí),可以調(diào)用 CancelFunc
函數(shù),以通知相關(guān)的操作停止工作。這個(gè)函數(shù)類型可以與 context.WithCancel
、context.WithDeadline
、context.WithTimeout
等函數(shù)一起使用,這些函數(shù)在創(chuàng)建派生的上下文時(shí)會(huì)返回一個(gè) CancelFunc
。
需要注意的是,調(diào)用 CancelFunc
只是向操作發(fā)送一個(gè)取消信號(hào),具體的操作是否真正停止取決于操作本身的實(shí)現(xiàn)。CancelFunc
的調(diào)用不會(huì)阻塞,它會(huì)立即返回。
在使用 CancelFunc
時(shí),應(yīng)注意并發(fā)調(diào)用的情況,因?yàn)樗梢员欢鄠€(gè) goroutine 同時(shí)調(diào)用。確保在并發(fā)情況下正確處理取消操作,以避免潛在的競態(tài)條件和問題。
6. cancleCtx
?type cancelCtx struct { ? Context ?? ? mu ? ? ? sync.Mutex ? ? ? ? ? ?// protects following fields ? done ? ? atomic.Value ? ? ? ? ?// of chan struct{}, created lazily, closed by first cancel call ? children map[canceler]struct{} // set to nil by the first cancel call ? err ? ? ?error ? ? ? ? ? ? ? ? // set to non-nil by the first cancel call ? cause ? ?error ? ? ? ? ? ? ? ? // set to non-nil by the first cancel call ?}
cancelCtx
是 context
包中定義的一個(gè)結(jié)構(gòu)體類型,實(shí)現(xiàn)了 Context
接口。它是基于取消機(jī)制的上下文類型,用于表示可以被取消的上下文。
下面是 cancelCtx
結(jié)構(gòu)體的詳細(xì)介紹:
Context
:嵌入字段,表示cancelCtx
結(jié)構(gòu)體實(shí)現(xiàn)了Context
接口,可以被用作上下文對象。mu
:sync.Mutex
類型的互斥鎖,用于保護(hù)以下字段的并發(fā)訪問。done
:atomic.Value
類型的原子值,用于存儲(chǔ)一個(gè)chan struct{}
,該通道在首次取消調(diào)用時(shí)被關(guān)閉。該字段是惰性創(chuàng)建的,即在首次取消調(diào)用之前為nil
。它用于通知相關(guān)的 goroutine 上下文已被取消。children
:map[canceler]struct{}
類型的字段,用于存儲(chǔ)與該上下文關(guān)聯(lián)的子上下文(通過WithCancel
、WithDeadline
或WithTimeout
創(chuàng)建)。子上下文被存儲(chǔ)在該映射中作為鍵,對應(yīng)的值為空結(jié)構(gòu)。在首次取消調(diào)用后,該字段會(huì)被設(shè)置為nil
。err
:error
類型的字段,用于存儲(chǔ)上下文的取消錯(cuò)誤。在首次取消調(diào)用后,該字段會(huì)被設(shè)置為非nil
的錯(cuò)誤值。cause
:error
類型的字段,用于存儲(chǔ)上下文的取消原因。在首次取消調(diào)用后,該字段會(huì)被設(shè)置為非nil
的錯(cuò)誤值。
cancelCtx
結(jié)構(gòu)體用于實(shí)現(xiàn)上下文的取消機(jī)制。當(dāng)調(diào)用 cancel
方法時(shí),會(huì)通過關(guān)閉 done
通道來通知相關(guān)的 goroutine 上下文已被取消。同時(shí),會(huì)設(shè)置 err
字段為取消錯(cuò)誤,并記錄取消原因(如果提供了)。子上下文也會(huì)被取消,即它們的 cancel
方法會(huì)被調(diào)用,并將自身從 children
映射中刪除。
這種基于取消機(jī)制的上下文類型可以用于在多個(gè) goroutine 之間傳遞取消信號(hào),使得相關(guān)的操作可以在需要時(shí)及時(shí)終止。
7. WithCancel
?func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { ? c := withCancel(parent) ? return c, func() { c.cancel(true, Canceled, nil) } ?}
WithCancel
函數(shù)的主要特點(diǎn)和步驟:
- 返回一個(gè)基于父上下文的副本,并創(chuàng)建一個(gè)新的
Done
通道。 - 當(dāng)調(diào)用返回的
cancel
函數(shù)或者父上下文的Done
通道關(guān)閉時(shí),返回的上下文的Done
通道也會(huì)被關(guān)閉。 - 調(diào)用
cancel
函數(shù)會(huì)釋放與上下文相關(guān)的資源,因此在操作完成后應(yīng)盡快調(diào)用。 - 使用
WithCancel
創(chuàng)建的上下文可以手動(dòng)取消操作,并確保及時(shí)釋放資源。
?func Cancel() { ? // 創(chuàng)建一個(gè)帶有取消功能的上下文 ? ctx, cancel := context.WithCancel(context.Background()) ?? ? // 啟動(dòng)一個(gè) goroutine 來執(zhí)行定時(shí)任務(wù) ? go func() { ? for { ? select { ? case <-ctx.Done(): ? fmt.Println("定時(shí)任務(wù)被取消") ? return ? default: ? // 模擬定時(shí)任務(wù)的工作 ? fmt.Println("執(zhí)行定時(shí)任務(wù)...") ? time.Sleep(1 * time.Second) ? } ? } ? }() ?? ? // 模擬等待一段時(shí)間后取消定時(shí)任務(wù) ? time.Sleep(5 * time.Second) ? cancel() ?? ? // 等待一段時(shí)間以觀察任務(wù)的狀態(tài) ? time.Sleep(2 * time.Second) ?} ??
wangyufan@wangcomputerair MINGW64 /d/goworkplace/src/github.com/context (master)
$ go run .
執(zhí)行定時(shí)任務(wù)...
執(zhí)行定時(shí)任務(wù)...
執(zhí)行定時(shí)任務(wù)...
執(zhí)行定時(shí)任務(wù)...
執(zhí)行定時(shí)任務(wù)...
定時(shí)任務(wù)被取消
在這個(gè)例子中,我們創(chuàng)建了一個(gè)帶有取消功能的上下文(ctx
)和對應(yīng)的取消函數(shù)(cancel
)。然后,我們啟動(dòng)了一個(gè) goroutine 來執(zhí)行定時(shí)任務(wù),每隔一秒鐘輸出一次"執(zhí)行定時(shí)任務(wù)..."。在主函數(shù)中,我們等待了5秒鐘后調(diào)用cancel
函數(shù),發(fā)送取消信號(hào)。最后,我們等待2秒鐘以觀察任務(wù)的狀態(tài)。
通過運(yùn)行這個(gè)示例,你可以觀察到在取消信號(hào)發(fā)送后,定時(shí)任務(wù)會(huì)立即停止執(zhí)行,輸出"定時(shí)任務(wù)被取消",而不會(huì)繼續(xù)執(zhí)行剩余的定時(shí)任務(wù)。這展示了使用context.WithCancel()
取消任務(wù)的現(xiàn)象明顯的例子
8. WithDeadline
?func WithDeadline(parent Context, d time.Time) (Context, CancelFunc){}
WithDeadline
函數(shù)返回一個(gè)基于父上下文 parent
的副本,并設(shè)置一個(gè)截止時(shí)間 d
。返回的上下文對象具有一個(gè)新的 Done
通道,當(dāng)截止時(shí)間到達(dá)或者父上下文的 Done
通道關(guān)閉時(shí)(以先發(fā)生者為準(zhǔn)),該 Done
通道將被關(guān)閉。
取消該上下文會(huì)釋放與其相關(guān)聯(lián)的資源,因此,在使用該上下文執(zhí)行的操作完成后,代碼應(yīng)盡快調(diào)用 cancel
函數(shù)。
WithDeadline
函數(shù)的作用是創(chuàng)建一個(gè)帶有截止時(shí)間的上下文??梢允褂迷撋舷挛膩砜刂撇僮鞯膱?zhí)行時(shí)間,一旦截止時(shí)間到達(dá),相關(guān)操作可以被取消或中止。
需要注意的是,截止時(shí)間是一個(gè)絕對時(shí)間,可以使用 time.Now()
結(jié)合 time.Duration
來指定一個(gè)相對于當(dāng)前時(shí)間的截止時(shí)間。
WithDeadline
函數(shù)的主要特點(diǎn)和步驟如下:
- 返回一個(gè)基于父上下文的副本,并設(shè)置截止時(shí)間為
d
。 - 創(chuàng)建一個(gè)新的
Done
通道,當(dāng)截止時(shí)間到達(dá)或者父上下文的Done
通道關(guān)閉時(shí),該Done
通道將被關(guān)閉。 - 調(diào)用
cancel
函數(shù)會(huì)釋放與上下文相關(guān)的資源,因此在操作完成后應(yīng)盡快調(diào)用。 - 使用
WithDeadline
創(chuàng)建的上下文可以控制操作的執(zhí)行時(shí)間,并在截止時(shí)間到達(dá)時(shí)取消或中止相關(guān)操作。
?func Dead() { ? ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) ?? ? // 啟動(dòng)一個(gè) goroutine 來執(zhí)行任務(wù) ? go func() { ? for { ? select { ? case <-ctx.Done(): ? fmt.Println("任務(wù)被取消:", ctx.Err()) ? return ? default: ? // 模擬任務(wù)的工作 ? fmt.Println("執(zhí)行任務(wù)...") ? time.Sleep(1 * time.Second) ? } ? } ? }() ?? ? // 等待一段時(shí)間以觀察任務(wù)的狀態(tài) ? time.Sleep(8 * time.Second) ?? ? // 取消任務(wù) ? cancel() ?? ? // 等待一段時(shí)間以觀察任務(wù)的狀態(tài) ? time.Sleep(2 * time.Second) ?}
wangyufan@wangcomputerair MINGW64 /d/goworkplace/src/github.com/context (master)
$ go run .
執(zhí)行任務(wù)...
執(zhí)行任務(wù)...
執(zhí)行任務(wù)...
執(zhí)行任務(wù)...
執(zhí)行任務(wù)...
任務(wù)被取消: context deadline exceeded
在這個(gè)示例中,我們使用context.WithDeadline()
創(chuàng)建了一個(gè)具有截止時(shí)間的上下文(ctx
)和對應(yīng)的取消函數(shù)(cancel
)。通過調(diào)用time.Now().Add(5*time.Second)
,我們設(shè)置了截止時(shí)間為當(dāng)前時(shí)間5秒后。然后,我們啟動(dòng)了一個(gè) goroutine 來執(zhí)行任務(wù),每隔一秒鐘輸出一次"執(zhí)行任務(wù)..."。在主函數(shù)中,我們等待了8秒鐘,超過了截止時(shí)間,然后調(diào)用cancel
函數(shù)取消任務(wù)。最后,我們等待2秒鐘以觀察任務(wù)的狀態(tài)。
通過運(yùn)行這個(gè)示例,你可以觀察到在截止時(shí)間到達(dá)后,任務(wù)會(huì)立即停止執(zhí)行,并輸出"任務(wù)被取消"。這展示了使用context.WithDeadline()
設(shè)置截止時(shí)間并取消任務(wù)的示例。
9. WithTimeOut
?func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { ? return WithDeadline(parent, time.Now().Add(timeout)) ?}
WithTimeout
函數(shù)是 context
包中的一個(gè)輔助函數(shù),它基于父上下文 parent
和超時(shí)時(shí)間 timeout
創(chuàng)建一個(gè)新的上下文,并返回該上下文以及對應(yīng)的取消函數(shù)。
函數(shù)的內(nèi)部實(shí)現(xiàn)是調(diào)用了 WithDeadline
函數(shù),將當(dāng)前時(shí)間加上超時(shí)時(shí)間得到截止時(shí)間,然后將截止時(shí)間作為參數(shù)調(diào)用 WithDeadline
,返回相應(yīng)的上下文和取消函數(shù)。
簡而言之,WithTimeout
函數(shù)是使用相對于當(dāng)前時(shí)間的超時(shí)時(shí)間來創(chuàng)建一個(gè)帶有截止時(shí)間的上下文。超時(shí)時(shí)間可以是一個(gè)持續(xù)時(shí)間(time.Duration
)對象,表示從當(dāng)前時(shí)間開始的一段時(shí)間。
使用 WithTimeout
函數(shù)可以方便地創(chuàng)建一個(gè)具有超時(shí)控制的上下文,以確保在超時(shí)時(shí)間到達(dá)后相關(guān)操作可以被取消或中止。
需要注意的是,超時(shí)時(shí)間應(yīng)該是一個(gè)非負(fù)值。如果超時(shí)時(shí)間為零或負(fù)值,表示立即超時(shí),即操作將立即取消。
?// TimeOut the function that will be testing the context.WithTimeout ?func TimeOut() { ? // 設(shè)置一個(gè)超時(shí)時(shí)間為5秒的上下文 ? ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ?? ? // 啟動(dòng)一個(gè) goroutine 來執(zhí)行任務(wù) ? go func() { ? for { ? select { ? case <-ctx.Done(): ? fmt.Println("任務(wù)被取消:", ctx.Err()) ? return ? default: ? // 模擬任務(wù)的工作 ? fmt.Println("執(zhí)行任務(wù)...") ? time.Sleep(1 * time.Second) ? } ? } ? }() ?? ? // 等待一段時(shí)間以觀察任務(wù)的狀態(tài) ? time.Sleep(8 * time.Second) ?? ? // 取消任務(wù) ? cancel() ?? ? // 等待一段時(shí)間以觀察任務(wù)的狀態(tài) ? time.Sleep(2 * time.Second) ?} ??
wangyufan@wangcomputerair MINGW64 /d/goworkplace/src/github.com/context (master)
$ go run .
執(zhí)行任務(wù)...
執(zhí)行任務(wù)...
執(zhí)行任務(wù)...
執(zhí)行任務(wù)...
執(zhí)行任務(wù)...
任務(wù)被取消: context deadline exceeded
在這個(gè)示例中,我們使用context.WithTimeout()
創(chuàng)建了一個(gè)具有超時(shí)時(shí)間的上下文(ctx
)和對應(yīng)的取消函數(shù)(cancel
)。通過設(shè)置超時(shí)時(shí)間為5秒(5*time.Second
),我們限定了任務(wù)的執(zhí)行時(shí)間。然后,我們啟動(dòng)了一個(gè) goroutine 來執(zhí)行任務(wù),每隔一秒鐘輸出一次"執(zhí)行任務(wù)..."。在主函數(shù)中,我們等待了8秒鐘,超過了超時(shí)時(shí)間,然后調(diào)用cancel
函數(shù)取消任務(wù)。最后,我們等待2秒鐘以觀察任務(wù)的狀態(tài)。
通過運(yùn)行這個(gè)示例,你可以觀察到在超時(shí)時(shí)間到達(dá)后,任務(wù)會(huì)立即停止執(zhí)行,并輸出"任務(wù)被取消"。這展示了使用context.WithTimeout()
設(shè)置超時(shí)時(shí)間并取消任務(wù)的示例。
10. WithValue
?func WithValue(parent Context, key, val any) Context { ? if parent == nil { ? panic("cannot create context from nil parent") ? } ? if key == nil { ? panic("nil key") ? } ? if !reflectlite.TypeOf(key).Comparable() { ? panic("key is not comparable") ? } ? return &valueCtx{parent, key, val} ?}
WithValue
函數(shù)是 context
包中的一個(gè)輔助函數(shù),用于基于父上下文 parent
創(chuàng)建一個(gè)新的上下文,并將鍵值對 (key, val)
存儲(chǔ)在新上下文中。函數(shù)返回新的上下文對象。
函數(shù)首先進(jìn)行了一些參數(shù)校驗(yàn),確保 parent
不為空,key
不為 nil
,且 key
是可比較的(comparable)。在 Go 語言中,可比較的類型是指可以使用 ==
運(yùn)算符進(jìn)行比較的類型。
然后,函數(shù)創(chuàng)建了一個(gè) valueCtx
結(jié)構(gòu)體,并將父上下文、key
和 val
存儲(chǔ)在該結(jié)構(gòu)體中。valueCtx
是 context
包中定義的一個(gè)內(nèi)部類型,用于存儲(chǔ)上下文的鍵值對信息。
最后,函數(shù)返回新創(chuàng)建的 valueCtx
對象,它實(shí)現(xiàn)了 Context
接口。
通過使用 WithValue
函數(shù),可以在上下文中存儲(chǔ)和傳遞與請求相關(guān)的數(shù)據(jù),這些數(shù)據(jù)可以被跨 API 邊界和進(jìn)程邊界傳遞。需要注意的是,WithValue
函數(shù)適用于傳遞請求范圍的數(shù)據(jù),而不應(yīng)該被用于傳遞可選的函數(shù)參數(shù)。
?func Test() { ? // 創(chuàng)建一個(gè)父級上下文 ? parent := context.Background() ?? ? // 使用 WithValue 創(chuàng)建一個(gè)帶有用戶身份信息的子級上下文 ? user := User{ID: 123, Name: "Alice"} ? ctx := context.WithValue(parent, "user", user) ?? ? // 在不同的函數(shù)中獲取用戶身份信息 ? processRequest(ctx) ?} ?? ?// processRequest a function that get information from ctx ?func processRequest(ctx context.Context) { ? // 從上下文中獲取用戶身份信息 ? user, ok := ctx.Value("user").(User) ? if !ok { ? fmt.Println("無法獲取用戶身份信息") ? return ? } ?? ? // 使用用戶身份信息執(zhí)行請求處理 ? fmt.Printf("處理請求,用戶ID: %d, 用戶名: %s\n", user.ID, user.Name) ?? ? // 調(diào)用其他函數(shù)傳遞上下文 ? otherFunction(ctx) ?} ?? ?// otherFunction another function that get information form ctx ?func otherFunction(ctx context.Context) { ? // 從上下文中獲取用戶身份信息 ? user, ok := ctx.Value("user").(User) ? if !ok { ? fmt.Println("無法獲取用戶身份信息") ? return ? } ?? ? // 使用用戶身份信息執(zhí)行其他操作 ? fmt.Printf("執(zhí)行其他操作,用戶ID: %d, 用戶名: %s\n", user.ID, user.Name) ?} ??
wangyufan@wangcomputerair MINGW64 /d/goworkplace/src/github.com/context (master)
$ go run .
處理請求,用戶ID: 123, 用戶名: Alice
執(zhí)行其他操作,用戶ID: 123, 用戶名: Alice
在這個(gè)例子中,我們首先創(chuàng)建了一個(gè)父級上下文parent
。然后,我們使用context.WithValue()
將用戶身份信息User{ID: 123, Name: "Alice"}
與上下文關(guān)聯(lián),創(chuàng)建了一個(gè)帶有用戶身份信息的子級上下文ctx
。接下來,我們通過調(diào)用processRequest(ctx)
將子級上下文傳遞給處理請求的函數(shù)。
在processRequest
函數(shù)中,我們從上下文中獲取用戶身份信息,并使用該信息執(zhí)行請求處理。然后,我們調(diào)用otherFunction(ctx)
將上下文傳遞給另一個(gè)函數(shù)。
在otherFunction
函數(shù)中,我們同樣從上下文中獲取用戶身份信息,并使用該信息執(zhí)行其他操作。
通過運(yùn)行這個(gè)例子,你可以觀察到在不同的函數(shù)中成功獲取到了用戶身份信息,并使用該信息進(jìn)行相應(yīng)的處理和操作。這展示了如何使用context.WithValue()
將用戶身份信息與上下文關(guān)聯(lián),并在不同的函數(shù)中傳遞和獲取這些信息。
以上就是一文讓你理解go語言的Context的詳細(xì)內(nèi)容,更多關(guān)于go Context的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
GoFrame框架gcache的緩存控制淘汰策略實(shí)踐示例
這篇文章主要為大家介紹了GoFrame框架gcache的緩存控制淘汰策略的實(shí)踐示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06利用GoLang?Fiber進(jìn)行高性能Web開發(fā)實(shí)例詳解
這篇文章主要為大家介紹了利用GoLang?Fiber進(jìn)行高性能Web開發(fā)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Golang使用bcrypt實(shí)現(xiàn)密碼加密和校驗(yàn)的操作代碼
bcrypt可以用于數(shù)據(jù)庫中的用戶密碼保存,相比md5而言更加的安全可靠,這篇文章主要介紹了Golang使用bcrypt實(shí)現(xiàn)密碼加密和校驗(yàn)的操作代碼,需要的朋友可以參考下2024-05-05golang程序進(jìn)度條實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了golang程序?qū)崿F(xiàn)進(jìn)度條示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08