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

