欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go中的Context實(shí)現(xiàn)原理以及正確使用方式

 更新時(shí)間:2024年11月06日 08:47:04   作者:Chicken Run  
在 Go 語(yǔ)言中,Context 包是一種非常常用的工具,它被用來(lái)管理 goroutine 之間的通信和取消,本文將深入探討Context 包的基本原理,包括使用場(chǎng)景、原理和一些最佳實(shí)踐,感興趣的小伙伴跟著小編一起來(lái)看看吧

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):

  1. 明確性:將context作為第一個(gè)參數(shù),清晰地表明了函數(shù)執(zhí)行的上下文依賴,增強(qiáng)了代碼的可讀性和意圖表達(dá)。這種方式符合Go的設(shè)計(jì)哲學(xué),即顯式優(yōu)于隱式。

  2. 易于測(cè)試:測(cè)試時(shí)可以輕松創(chuàng)建并傳遞一個(gè)自定義的context.Context實(shí)例,便于控制測(cè)試中的超時(shí)和取消邏輯,無(wú)需修改函數(shù)簽名或結(jié)構(gòu)體定義。

  3. 標(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):

  1. 參數(shù)列表增長(zhǎng):對(duì)于參數(shù)較多的函數(shù),額外增加一個(gè)context.Context可能會(huì)讓函數(shù)簽名顯得冗長(zhǎng),特別是當(dāng)多個(gè)函數(shù)嵌套調(diào)用時(shí),每一層都需要傳遞context。

  2. 侵入性:雖然增加了靈活性,但也意味著每一個(gè)需要考慮取消或超時(shí)邏輯的函數(shù)都需要調(diào)整,對(duì)于既有代碼庫(kù)的改造成本較高。

嵌入到結(jié)構(gòu)體中

優(yōu)點(diǎn):

  1. 減少函數(shù)簽名復(fù)雜度:將context.Context作為一個(gè)字段嵌入到結(jié)構(gòu)體中,可以減少函數(shù)參數(shù)的數(shù)量,使函數(shù)簽名更加簡(jiǎn)潔。

  2. 封裝性:對(duì)于內(nèi)部邏輯復(fù)雜的服務(wù),將context隱藏在結(jié)構(gòu)體內(nèi)部,可以對(duì)外提供更加抽象和友好的接口,提高代碼的封裝性。

缺點(diǎn):

  1. 測(cè)試復(fù)雜度增加:如果結(jié)構(gòu)體中的context字段不是公開(kāi)的,測(cè)試時(shí)可能需要通過(guò)構(gòu)造特定的結(jié)構(gòu)體實(shí)例來(lái)傳遞特定的上下文信息,這可能使得測(cè)試代碼變得復(fù)雜。

  2. 靈活性降低:一旦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)文章

最新評(píng)論