Go中的Context實(shí)現(xiàn)原理以及正確使用方式
1. 基本原理
1.1 Context 包的介紹
在 Go 語(yǔ)言中,Context 包是用于傳遞請(qǐng)求范圍數(shù)據(jù)、取消信號(hào)和截止時(shí)間的機(jī)制。它通常被用來(lái)處理 goroutine 之間的通信和取消。Context 包是 Go 語(yǔ)言內(nèi)置的,它可以很方便地使用,而不需要額外的依賴。
Context 包是一個(gè)輕量級(jí)的工具,它提供了一個(gè)標(biāo)準(zhǔn)的接口,用于在 goroutine 之間傳遞請(qǐng)求范圍的數(shù)據(jù)、取消信號(hào)和截止時(shí)間。Context 包實(shí)現(xiàn)了一種類似于樹(shù)狀結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu),其中每個(gè)節(jié)點(diǎn)都表示一個(gè)請(qǐng)求范圍。每個(gè)節(jié)點(diǎn)都有一個(gè)唯一的 key-value 對(duì),其中 key 是一個(gè) interface{} 類型的值,而 value 則是任何類型的值。Context 包還提供了一個(gè)可選的超時(shí)機(jī)制,用于在一定時(shí)間后自動(dòng)取消請(qǐng)求。
Context 包的核心是一個(gè) Context 接口,它定義了一些方法,用于獲取請(qǐng)求范圍數(shù)據(jù)、取消請(qǐng)求和處理超時(shí)。
type Context interface { Deadline() (deadline time.Time, ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
- Deadline() 方法返回截止時(shí)間和一個(gè)布爾值,指示截止時(shí)間是否已經(jīng)設(shè)置。
- Done() 方法返回一個(gè)只讀的 channel,當(dāng)請(qǐng)求被取消或超時(shí)時(shí),該 channel 將被關(guān)閉。
- Err() 方法返回一個(gè)錯(cuò)誤,指示為什么請(qǐng)求被取消。
- Value() 方法返回與給定key相關(guān)聯(lián)的值,如果沒(méi)有值,則返回 nil。
Context 包還提供了兩個(gè)用于創(chuàng)建 Context 的函數(shù):WithContext 和 Background。Background 函數(shù)返回一個(gè)空的 Context,而 WithContext 函數(shù)則根據(jù)給定的父 Context 創(chuàng)建一個(gè)新的 Context。
Context 包的基本原理是通過(guò)在 goroutine 之間傳遞 Context 來(lái)實(shí)現(xiàn)請(qǐng)求范圍數(shù)據(jù)、取消信號(hào)和截止時(shí)間的管理。當(dāng)一個(gè) goroutine 創(chuàng)建了一個(gè)新的 goroutine 時(shí),它將 Context 作為參數(shù)傳遞給新的 goroutine。新的goroutine 可以使用這個(gè) Context 來(lái)訪問(wèn)請(qǐng)求范圍數(shù)據(jù)、接收取消信號(hào)和處理超時(shí)。
1.2 Context 的創(chuàng)建
在 Golang 中,Context 可以通過(guò) WithCancel、WithDeadline、WithTimeout 和 WithValue 等函數(shù)來(lái)創(chuàng)建。下面分別介紹這些函數(shù)的用法和注意事項(xiàng)。
1.2.1 WithCancel
WithCancel 函數(shù)可以用于創(chuàng)建一個(gè) Context 對(duì)象,并返回一個(gè)可取消的上下文和一個(gè)取消函數(shù)。當(dāng)調(diào)用取消函數(shù)時(shí),會(huì)通知所有的 Context 對(duì)象和其子 Context 對(duì)象,使它們都取消執(zhí)行。
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
下面是一個(gè)示例代碼:
package main ? import ( "context" "fmt" "time" ) ? func main() { parent := context.Background() ctx, cancel := context.WithCancel(parent) go func() { select { case <-ctx.Done(): fmt.Println(ctx.Err()) return case <-time.After(5 * time.Second): fmt.Println("work done") } }() time.Sleep(10 * time.Second) cancel() time.Sleep(1 * time.Second) }
在上面的代碼中,我們首先使用 context.Background() 函數(shù)創(chuàng)建一個(gè)根 Context 對(duì)象 parent,然后使用 WithCancel 函數(shù)創(chuàng)建一個(gè)子 Context 對(duì)象 ctx,并返回一個(gè)可取消的上下文和一個(gè)取消函數(shù) cancel。接下來(lái),我們?cè)谝粋€(gè) goroutine 中使用 select 語(yǔ)句監(jiān)聽(tīng) Context 對(duì)象的 Done 方法和 time.After 函數(shù)的返回值,如果 Done 方法返回一個(gè)非 nil 的 error,則說(shuō)明 Context 已經(jīng)被取消,否則說(shuō)明 time.After 函數(shù)已經(jīng)超時(shí)。在主函數(shù)中,我們調(diào)用 cancel 函數(shù)來(lái)通知 Context 對(duì)象和其子 Context 對(duì)象,使它們都取消執(zhí)行。最后,我們使用 time.Sleep 函數(shù)讓程序等待一段時(shí)間,以便觀察 Context 的執(zhí)行情況。
1.2.2 WithDeadline
WithDeadline 函數(shù)可以用于創(chuàng)建一個(gè) Context 對(duì)象,并返回一個(gè)截止時(shí)間和一個(gè)取消函數(shù)。當(dāng)超過(guò)截止時(shí)間時(shí),會(huì)自動(dòng)通知所有的 Context 對(duì)象和其子 Context 對(duì)象,使它們都取消執(zhí)行。
func WithDeadline(parent Context, deadline time.Time) (ctx Context, cancel CancelFunc)
下面是一個(gè)示例代碼:
package main ? import ( "context" "fmt" "time" ) ? func main() { parent := context.Background() ctx, cancel := context.WithDeadline(parent, time.Now().Add(5*time.Second)) go func() { select { case <-ctx.Done(): fmt.Println(ctx.Err()) return case <-time.After(10 * time.Second): fmt.Println("work done") } }() time.Sleep(20 * time.Second) cancel() time.Sleep(1 * time.Second) }
在上面的代碼中,我們首先使用 context.Background() 函數(shù)創(chuàng)建一個(gè)根 Context 對(duì)象 parent,然后使用 WithDeadline 函數(shù)創(chuàng)建一個(gè)子 Context 對(duì)象 ctx,并返回一個(gè)截止時(shí)間和一個(gè)取消函數(shù) cancel。接下來(lái),我們?cè)谝粋€(gè) goroutine 中使用 select 語(yǔ)句監(jiān)聽(tīng) Context 對(duì)象的 Done 方法和 time.After 函數(shù)的返回值,如果 Done 方法返回一個(gè)非 nil 的 error,則說(shuō)明 Context 已經(jīng)被取消,否則說(shuō)明 time.After 函數(shù)已經(jīng)超時(shí)。在主函數(shù)中,我們調(diào)用 cancel 函數(shù)來(lái)通知 Context 對(duì)象和其子 Context 對(duì)象,使它們都取消執(zhí)行。最后,我們使用 time.Sleep 函數(shù)讓程序等待一段時(shí)間,以便觀察 Context 的執(zhí)行情況。
1.2.3 WithTimeout
WithTimeout 函數(shù)可以用于創(chuàng)建一個(gè) Context 對(duì)象,并返回一個(gè)超時(shí)時(shí)間和一個(gè)取消函數(shù)。當(dāng)超過(guò)超時(shí)時(shí)間時(shí),會(huì)自動(dòng)通知所有的 Context 對(duì)象和其子 Context 對(duì)象,使它們都取消執(zhí)行。
func WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)
下面是一個(gè)示例代碼:
package main ? import ( "context" "fmt" "time" ) ? func main() { parent := context.Background() ctx, cancel := context.WithTimeout(parent, 5*time.Second) go func() { select { case <-ctx.Done(): fmt.Println(ctx.Err()) return case <-time.After(10 * time.Second): fmt.Println("work done") } }() time.Sleep(20 * time.Second) cancel() time.Sleep(1 * time.Second) }
在上面的代碼中,我們首先使用 context.Background() 函數(shù)創(chuàng)建一個(gè)根 Context 對(duì)象 parent,然后使用 WithTimeout 函數(shù)創(chuàng)建一個(gè)子 Context 對(duì)象 ctx,并返回一個(gè)超時(shí)時(shí)間和一個(gè)取消函數(shù) cancel。接下來(lái),我們?cè)谝粋€(gè) goroutine 中使用 select 語(yǔ)句監(jiān)聽(tīng) Context 對(duì)象的 Done 方法和 time.After 函數(shù)的返回值,如果 Done 方法返回一個(gè)非 nil 的 error,則說(shuō)明 Context 已經(jīng)被取消,否則說(shuō)明 time.After 函數(shù)已經(jīng)超時(shí)。在主函數(shù)中,我們調(diào)用 cancel 函數(shù)來(lái)通知 Context 對(duì)象和其子 Context 對(duì)象,使它們都取消執(zhí)行。最后,我們使用 time.Sleep 函數(shù)讓程序等待一段時(shí)間,以便觀察 Context 的執(zhí)行情況。
1.2.4 WithValue
WithValue 函數(shù)可以用于創(chuàng)建一個(gè) Context 對(duì)象,并返回一個(gè)包含指定值的 Context 對(duì)象。這個(gè)值可以是任意類型的數(shù)據(jù),可以是基本類型、結(jié)構(gòu)體或者指針等。需要注意的是,這個(gè)值只在當(dāng)前 Context 對(duì)象及其子 Context 對(duì)象中有效,對(duì)于其他 Context 對(duì)象來(lái)說(shuō)是不可見(jiàn)的。
func WithValue(parent Context, key interface{}, val interface{}) Context
下面是一個(gè)示例代碼:
package main ? import ( "context" "fmt" ) ? type userKey struct{} ? func main() { parent := context.Background() ctx := context.WithValue(parent, userKey{}, "admin") go func() { if user, ok := ctx.Value(userKey{}).(string); ok { fmt.Printf("user is %s\n", user) } else { fmt.Println("user is not found") } }() select {} }
在上面的代碼中,我們首先使用 context.Background() 函數(shù)創(chuàng)建一個(gè)根 Context 對(duì)象 parent,然后使用 WithValue 函數(shù)創(chuàng)建一個(gè)子 Context 對(duì)象 ctx,并返回一個(gè)包含指定值的 Context 對(duì)象。接下來(lái),我們?cè)谝粋€(gè) goroutine 中使用 ctx.Value 函數(shù)獲取 Context 對(duì)象中的值,并判斷其類型是否為字符串類型。如果是,則輸出其值,否則輸出 “user is not found”。在主函數(shù)中,我們使用select語(yǔ)句使程序一直運(yùn)行,以便觀察 Context 的執(zhí)行情況。
2. Context 的使用場(chǎng)景
2.1 并發(fā)控制
一個(gè)很典型的使用場(chǎng)景是,當(dāng)我們需要同時(shí)啟動(dòng)多個(gè) goroutine 進(jìn)行任務(wù)處理時(shí),我們可以使用 Context 來(lái)控制這些 goroutine 的執(zhí)行。在每個(gè) goroutine 中,我們都可以檢測(cè) Context 對(duì)象是否被取消,如果是,則退出 goroutine 的執(zhí)行,否則繼續(xù)執(zhí)行。
下面是一個(gè)示例代碼:
package main ? import ( "context" "fmt" "sync" ) ? func worker(ctx context.Context, wg *sync.WaitGroup) { defer wg.Done() for { select { default: fmt.Println("work") case <-ctx.Done(): return } } } ? func main() { parent := context.Background() ctx, cancel := context.WithCancel(parent) var wg sync.WaitGroup for i := 0; i < 3; i++ { wg.Add(1) go worker(ctx, &wg) } cancel() wg.Wait() }
在上面的代碼中,我們首先使用 context.Background() 函數(shù)創(chuàng)建一個(gè)根 Context 對(duì)象 parent,然后使用 WithCancel 函數(shù)創(chuàng)建一個(gè)子 Context 對(duì)象 ctx,并返回一個(gè)取消函數(shù) cancel。接下來(lái),我們使用 sync.WaitGroup 來(lái)等待所有的 goroutine 執(zhí)行完成。在主函數(shù)中,我們啟動(dòng)了三個(gè) goroutine 來(lái)執(zhí)行任務(wù),同時(shí)使用 cancel 函數(shù)來(lái)通知這些 goroutine 取消執(zhí)行。最后,我們使用Wait方法等待所有的 goroutine 執(zhí)行完成。
2.2 超時(shí)控制
另一個(gè)典型的使用場(chǎng)景是,當(dāng)我們需要對(duì)一個(gè)操作設(shè)置一個(gè)超時(shí)時(shí)間時(shí),我們可以使用 Context 來(lái)控制這個(gè)操作的執(zhí)行時(shí)間。在操作執(zhí)行超時(shí)時(shí),我們可以通知 Context 對(duì)象和其子 Context 對(duì)象取消執(zhí)行。
下面是一個(gè)示例代碼:
package main ? import ( "context" "fmt" "time" ) ? func work(ctx context.Context) { for { select { default: fmt.Println("work") case <-ctx.Done(): fmt.Println("work done") return } } } func main() { parent := context.Background() ctx, cancel := context.WithTimeout(parent, time.Second*5) defer cancel() work(ctx) }
在上面的代碼中,我們首先使用 context.Background() 函數(shù)創(chuàng)建一個(gè)根 Context 對(duì)象 parent,然后使用 WithTimeout 函數(shù)創(chuàng)建一個(gè)子 Context 對(duì)象 ctx,并返回一個(gè)取消函數(shù) cancel。在 work 函數(shù)中,我們啟動(dòng)一個(gè)無(wú)限循環(huán),不斷輸出 “work”。同時(shí),我們使用 select 語(yǔ)句來(lái)等待 Context 對(duì)象被取消。在主函數(shù)中,我們使用 defer 語(yǔ)句調(diào)用 cancel 函數(shù),以確保 Context 對(duì)象被取消。由于我們?cè)?WithTimeout 函數(shù)中設(shè)置了一個(gè) 5 秒的超時(shí)時(shí)間,因此當(dāng)程序運(yùn)行超過(guò) 5 秒時(shí),work 函數(shù)就會(huì)停止執(zhí)行。
2.3 數(shù)據(jù)庫(kù)連接
在使用數(shù)據(jù)庫(kù)連接時(shí),我們通常需要保證連接池中的連接數(shù)量不會(huì)超過(guò)一定的閾值。如果連接池中的連接數(shù)量超過(guò)了閾值,則需要等待連接釋放后再進(jìn)行操作。在這種情況下,我們可以使用 Context 來(lái)控制連接的生命周期。
下面是一個(gè)示例代碼:
package main ? import ( "context" "database/sql" "fmt" "sync" "time" ? _ "github.com/go-sql-driver/mysql" ) ? const maxConn = 5 ? func main() { db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test") if err != nil { panic(err) } defer db.Close() ? ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() ? connCh := make(chan *sql.Conn, maxConn) var wg sync.WaitGroup for i := 0; i < maxConn; i++ { wg.Add(1) go func() { defer wg.Done() for { select { case <-ctx.Done(): return default: if len(connCh) < maxConn { conn, err := db.Conn(ctx) if err != nil { fmt.Println(err) return } connCh <- conn } } } }() } wg.Wait() }
在上面的代碼中,我們首先使用 sql.Open 函數(shù)打開(kāi)一個(gè) MySQL 數(shù)據(jù)庫(kù)的連接,并返回一個(gè) DB 對(duì)象 db。接下來(lái),我們使用 WithTimeout 函數(shù)創(chuàng)建一個(gè) Context 對(duì)象 ctx,并設(shè)置一個(gè)超時(shí)時(shí)間為 5 秒。同時(shí),我們創(chuàng)建一個(gè)容量為 maxConn 的 channel 對(duì)象 connCh,用于存儲(chǔ)數(shù)據(jù)庫(kù)連接。在g oroutine 中,我們使用 select 語(yǔ)句等待 Context 對(duì)象被取消。在每次循環(huán)中,我們檢查連接池中連接的數(shù)量是否超過(guò)了閾值,如果沒(méi)有,則使用 db.Conn 函數(shù)從連接池中獲取一個(gè)新的連接,并將其存儲(chǔ)到 connCh 中。最后,我們使用 sync.WaitGroup 等待所有的 goroutine 執(zhí)行完成。
2.4 HTTP 請(qǐng)求
在使用 HTTP 請(qǐng)求時(shí),我們通常需要設(shè)置一個(gè)超時(shí)時(shí)間,以確保請(qǐng)求能夠在規(guī)定的時(shí)間內(nèi)得到響應(yīng)。在這種情況下,我們可以使用 Context 來(lái)控制HTTP請(qǐng)求的執(zhí)行時(shí)間。
下面是一個(gè)示例代碼:
package main ? import ( "context" "fmt" "io/ioutil" "net/http" "time" ) ? func main() { client := http.DefaultClient ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.example.com", nil) if err != nil { fmt.Println(err) return } ? resp, err := client.Do(req) if err != nil { fmt.Println(err) return } defer resp.Body.Close() ? body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) return } ? fmt.Println(string(body)) }
在上面的代碼中,我們首先使用 http.DefaultClient 創(chuàng)建一個(gè) HTTP 客戶端對(duì)象 client。接下來(lái),我們使用 WithTimeout 函數(shù)創(chuàng)建一個(gè) Context 對(duì)象 ctx,并設(shè)置一個(gè)超時(shí)時(shí)間為 5 秒。同時(shí),我們使用 http.NewRequestWithContext 函數(shù)創(chuàng)建一個(gè) HTTP 請(qǐng)求對(duì)象 req,并將 Context 對(duì)象 ctx 作為參數(shù)傳遞給該函數(shù)。在 Do 函數(shù)中,我們會(huì)自動(dòng)將 Context 對(duì)象 ctx 傳遞給 HTTP 請(qǐng)求,并在超時(shí)時(shí)間到達(dá)后自動(dòng)取消該請(qǐng)求。
2.5 gRPC 請(qǐng)求
在使用 gRPC 請(qǐng)求時(shí),我們通常需要設(shè)置一個(gè)超時(shí)時(shí)間,以確保請(qǐng)求能夠在規(guī)定的時(shí)間內(nèi)得到響應(yīng)。在這種情況下,我們可以使用 Context 來(lái)控制 gRPC 請(qǐng)求的執(zhí)行時(shí)間。
下面是一個(gè)示例代碼:
package main ? import ( "context" "fmt" "log" "time" ? pb "github.com/example/helloworld" "google.golang.org/grpc" ) ? const ( address = "localhost:50051" defaultName = "world" ) ? func main() { conn, err := grpc.Dial(address, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() ? c := pb.NewGreeterClient(conn) ? ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() ? r, err := c.SayHello(ctx, &pb.HelloRequest{Name: defaultName}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %s", r.GetMessage()) }
在上面的代碼中,我們首先使用 grpc.Dial 函數(shù)創(chuàng)建一個(gè) gRPC 客戶端連接對(duì)象 conn。接下來(lái),我們使用 pb.NewGreeterClient 函數(shù)創(chuàng)建一個(gè) GreeterClient 對(duì)象 c。然后,我們使用 WithTimeout 函數(shù)創(chuàng)建一個(gè) Context 對(duì)象 ctx,并設(shè)置一個(gè)超時(shí)時(shí)間為 5 秒。最后,我們使用 GreeterClient 對(duì)象 c 的 SayHello 函數(shù)發(fā)送一個(gè) gRPC 請(qǐng)求,并將 Context 對(duì)象 ctx 作為參數(shù)傳遞給該函數(shù)。在 SayHello 函數(shù)中,我們會(huì)自動(dòng)將 Context 對(duì)象 ctx 傳遞給 gRPC 請(qǐng)求,并在超時(shí)時(shí)間到達(dá)后自動(dòng)取消該請(qǐng)求。
Go Context 到底放第一個(gè)參數(shù)傳,還是放結(jié)構(gòu)體里?
作為函數(shù)的第一個(gè)參數(shù)
優(yōu)點(diǎn):
明確性:將
context
作為第一個(gè)參數(shù),清晰地表明了函數(shù)執(zhí)行的上下文依賴,增強(qiáng)了代碼的可讀性和意圖表達(dá)。這種方式符合Go的設(shè)計(jì)哲學(xué),即顯式優(yōu)于隱式。易于測(cè)試:測(cè)試時(shí)可以輕松創(chuàng)建并傳遞一個(gè)自定義的
context.Context
實(shí)例,便于控制測(cè)試中的超時(shí)和取消邏輯,無(wú)需修改函數(shù)簽名或結(jié)構(gòu)體定義。標(biāo)準(zhǔn)一致性:Go的標(biāo)準(zhǔn)庫(kù)廣泛采用了這種模式,比如
net/http
包中的ServeHTTP
方法。遵循這一標(biāo)準(zhǔn)使得代碼風(fēng)格統(tǒng)一,便于其他開(kāi)發(fā)者理解和維護(hù)。
缺點(diǎn):
參數(shù)列表增長(zhǎng):對(duì)于參數(shù)較多的函數(shù),額外增加一個(gè)
context.Context
可能會(huì)讓函數(shù)簽名顯得冗長(zhǎng),特別是當(dāng)多個(gè)函數(shù)嵌套調(diào)用時(shí),每一層都需要傳遞context
。侵入性:雖然增加了靈活性,但也意味著每一個(gè)需要考慮取消或超時(shí)邏輯的函數(shù)都需要調(diào)整,對(duì)于既有代碼庫(kù)的改造成本較高。
嵌入到結(jié)構(gòu)體中
優(yōu)點(diǎn):
減少函數(shù)簽名復(fù)雜度:將
context.Context
作為一個(gè)字段嵌入到結(jié)構(gòu)體中,可以減少函數(shù)參數(shù)的數(shù)量,使函數(shù)簽名更加簡(jiǎn)潔。封裝性:對(duì)于內(nèi)部邏輯復(fù)雜的服務(wù),將
context
隱藏在結(jié)構(gòu)體內(nèi)部,可以對(duì)外提供更加抽象和友好的接口,提高代碼的封裝性。
缺點(diǎn):
測(cè)試復(fù)雜度增加:如果結(jié)構(gòu)體中的
context
字段不是公開(kāi)的,測(cè)試時(shí)可能需要通過(guò)構(gòu)造特定的結(jié)構(gòu)體實(shí)例來(lái)傳遞特定的上下文信息,這可能使得測(cè)試代碼變得復(fù)雜。靈活性降低:一旦
context
作為結(jié)構(gòu)體的一部分,函數(shù)調(diào)用時(shí)就失去了直接控制context
的能力,比如無(wú)法在運(yùn)行時(shí)輕易改變超時(shí)時(shí)間或取消策略。
實(shí)踐建議
常規(guī)操作:對(duì)于大多數(shù)情況,遵循Go標(biāo)準(zhǔn)庫(kù)的推薦,將
context
作為函數(shù)的第一個(gè)參數(shù)傳遞是最佳選擇。這樣做既體現(xiàn)了Go的簡(jiǎn)潔和明了,也便于維護(hù)和測(cè)試。高度封裝的服務(wù):在設(shè)計(jì)高度封裝的內(nèi)部服務(wù)或復(fù)雜的API時(shí),可以考慮將
context
嵌入到結(jié)構(gòu)體中,尤其是當(dāng)需要在整個(gè)服務(wù)生命周期內(nèi)管理上下文時(shí)。但需權(quán)衡好封裝性和測(cè)試便利性之間的關(guān)系。混合使用:在某些場(chǎng)景下,你可能會(huì)發(fā)現(xiàn)結(jié)合兩者使用的效果更好。例如,在服務(wù)初始化階段,將
context
作為結(jié)構(gòu)體字段管理,而在服務(wù)的具體操作函數(shù)中,依然將context
作為第一個(gè)參數(shù)傳遞,以保持操作的靈活性。
總之,選擇將context
放在哪里,應(yīng)基于項(xiàng)目的具體需求、代碼的可讀性和維護(hù)性綜合考量。無(wú)論哪種方式,關(guān)鍵在于理解并充分利用context
機(jī)制,以提升程序的健壯性和可維護(hù)性。
以上就是Go中的Context實(shí)現(xiàn)原理以及正確使用方式的詳細(xì)內(nèi)容,更多關(guān)于Go Context實(shí)現(xiàn)原理及使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
基于Golang設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng)
這篇文章主要為大家學(xué)習(xí)介紹了如何基于Golang設(shè)計(jì)一套可控的定時(shí)任務(wù)系統(tǒng),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-07-07基于golang中container/list包的用法說(shuō)明
這篇文章主要介紹了基于golang中container/list包的用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04go實(shí)現(xiàn)服務(wù)優(yōu)雅關(guān)閉的示例
本文主要介紹了go實(shí)現(xiàn)服務(wù)優(yōu)雅關(guān)閉的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02Golang實(shí)現(xiàn)內(nèi)網(wǎng)穿透詳解
這篇文章主要為大家詳細(xì)介紹了Golang實(shí)現(xiàn)內(nèi)網(wǎng)穿透的相關(guān)知識(shí),包括原理和代碼實(shí)現(xiàn),文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考一下2024-11-11go語(yǔ)言中json數(shù)據(jù)的讀取和寫出操作
這篇文章主要介紹了go語(yǔ)言中json數(shù)據(jù)的讀取和寫出操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04Win7環(huán)境下搭建Go開(kāi)發(fā)環(huán)境(基于VSCode編輯器)
這篇文章主要介紹了Win7環(huán)境下搭建Go開(kāi)發(fā)環(huán)境(基于VSCode編輯器),需要的朋友可以參考下2017-02-02go實(shí)現(xiàn)自動(dòng)復(fù)制U盤小工具demo
這篇文章主要為大家介紹了go實(shí)現(xiàn)自動(dòng)復(fù)制U盤小工具demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12go語(yǔ)言題解LeetCode1275找出井字棋的獲勝者示例
這篇文章主要為大家介紹了go語(yǔ)言題解LeetCode1275找出井字棋的獲勝者示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01