context
context包定義了上下文類型,該類型在API邊界之間以及進(jìn)程之間傳遞截止日期嘲碧,取消信號和其他請求范圍的值嘴纺。
對服務(wù)器的傳入請求應(yīng)創(chuàng)建一個(gè)Context邓深,而對服務(wù)器的傳出調(diào)用應(yīng)接受一個(gè)Context。它們之間的函數(shù)調(diào)用鏈必須傳播Context衙吩,可以選擇將其替換為使用WithCancel互妓,WithDeadline,WithTimeout或WithValue創(chuàng)建的派生Context坤塞。取消context后冯勉,從該Context派生的所有Context也會被取消。
WithCancel摹芙,WithDeadline和WithTimeout函數(shù)采用Context(父級)并返回派生的Context(子級)和CancelFunc灼狰。調(diào)用CancelFunc會取消該子代及其子代,刪除父代對該子代的引用浮禾,并停止所有關(guān)聯(lián)的計(jì)時(shí)器交胚。未能調(diào)用CancelFunc會使子代及其子代泄漏份汗,直到父代被取消或計(jì)時(shí)器觸發(fā)。審核工具檢查所有控制流路徑上是否都使用了CancelFuncs蝴簇。
使用Context的程序應(yīng)遵循以下規(guī)則杯活,以使各個(gè)包之間的接口保持一致,并啟用靜態(tài)分析工具來檢查上下文傳播:
- 不要將Context存儲在結(jié)構(gòu)類型中军熏;
- 將Context明確傳遞給需要它的每個(gè)函數(shù)轩猩;
- Context應(yīng)該是第一個(gè)參數(shù),通常命名為ctx:
func DoSomething(ctx context.Context, arg Arg) error { // ... use ctx ... }
即使函數(shù)允許荡澎,也不要傳遞nil Context均践。如果不確定使用哪個(gè)上下文,請傳遞context.TODO摩幔。
僅將Context值用于傳遞流程和API的請求范圍的數(shù)據(jù)彤委,而不用于將可選參數(shù)傳遞給函數(shù)。
可以將相同的Context傳遞給在不同goroutine中運(yùn)行的函數(shù)或衡。Context對于由多個(gè)goroutine同時(shí)使用是安全的焦影。
Variables
var Canceled = errors.New("context canceled")
var DeadlineExceeded error = deadlineExceededError{}
Canceled是Context.Err類型,取消Context時(shí)返回的錯(cuò)誤封断。
DeadlineExceeded是上下文的最后期限過去時(shí)Context.Err返回的錯(cuò)誤斯辰。
type CancelFunc
type CancelFunc func()
CancelFunc告訴操作停止工作。 CancelFunc不等待工作停止坡疼。 在第一個(gè)調(diào)用之后彬呻,隨后對CancelFunc的調(diào)用什么都不做。
type Context
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
其中
Deadline
方法需要返回當(dāng)前Context
被取消的時(shí)間柄瑰,也就是完成工作的截止時(shí)間(deadline)闸氮;Done
方法需要返回一個(gè)Channel
,這個(gè)Channel
會在當(dāng)前工作完成或者上下文被取消之后關(guān)閉教沾,多次調(diào)用Done
方法會返回同一個(gè)Channel
蒲跨;Err
方法會返回當(dāng)前Context
結(jié)束的原因,它只會在Done
返回的Channel
被關(guān)閉時(shí)才會返回非空的值授翻;
- 如果當(dāng)前
Context
被取消就會返回Canceled
錯(cuò)誤或悲;- 如果當(dāng)前
Context
超時(shí)就會返回DeadlineExceeded
錯(cuò)誤;Value
方法會從Context
中返回鍵對應(yīng)的值堪唐,對于同一個(gè)上下文來說巡语,多次調(diào)用Value
并傳入相同的Key
會返回相同的結(jié)果,該方法僅用于傳遞跨API和進(jìn)程間跟請求域的數(shù)據(jù)羔杨;
func Background
func Background() Context
Background返回一個(gè)非空的Context捌臊。 它永遠(yuǎn)不會被取消,沒有值兜材,也沒有期限理澎。 它通常由主要功能逞力,初始化和測試使用,并用作傳入請求的頂級Context糠爬。
func TODO
func TODO() Context
TODO返回一個(gè)非空的Context寇荧。 當(dāng)不清楚要使用哪個(gè)上下文或尚不可用時(shí)(因?yàn)樯形磾U(kuò)展周圍的功能以接受Context參數(shù)),代碼應(yīng)使用context.TODO执隧。 靜態(tài)分析工具可識別TODO揩抡,該工具可確定上下文是否在程序中正確傳播。
func WithCancel
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
WithCancel返回具有新的“完成”通道的父級副本镀琉。 當(dāng)調(diào)用返回的cancel函數(shù)或關(guān)閉父上下文的Done通道時(shí)(以先發(fā)生的為準(zhǔn))峦嗤,關(guān)閉返回的上下文的Done通道。
取消此上下文將釋放與其關(guān)聯(lián)的資源屋摔,因此在此上下文中運(yùn)行的操作完成后烁设,代碼應(yīng)立即調(diào)用cancel。
func main() { gen := func(ctx context.Context) <-chan int { dst := make(chan int) n := 1 go func() { for { select { case <-ctx.Done(): return // returning not to leak the goroutine case dst <- n: n++ } } }() return dst } ctx, cancel := context.WithCancel(context.Background()) defer cancel() // cancel when we are finished consuming integers for n := range gen(ctx) { fmt.Println(n) if n == 5 { break } } }
上面的示例代碼中钓试,gen函數(shù)在單獨(dú)的goroutine中生成整數(shù)并將它們發(fā)送到返回的通道装黑。 gen的調(diào)用者在使用生成的整數(shù)之后需要取消上下文,以免gen啟動的內(nèi)部goroutine發(fā)生泄漏弓熏。
func WithDeadline
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
WithDeadline返回父上下文的副本恋谭,并將截止日期調(diào)整為不遲于d。 如果父母的截止日期早于d挽鞠,則WithDeadline(parent疚颊,d)在語義上等同于父母。 當(dāng)截止日期到期滞谢,調(diào)用返回的cancel函數(shù)或關(guān)閉父上下文的Done通道(以先到者為準(zhǔn))時(shí)串稀,將關(guān)閉返回的上下文的Done通道除抛。
取消此上下文將釋放與其關(guān)聯(lián)的資源狮杨,因此在此上下文中運(yùn)行的操作完成后,代碼應(yīng)立即調(diào)用cancel到忽。
func main() {
d := time.Now().Add(50 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), d)
// Even though ctx will be expired, it is good practice to call its
// cancelation function in any case. Failure to do so may keep the
// context and its parent alive longer than necessary.
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err())
}
}
上面的代碼中橄教,定義了一個(gè)50毫秒之后過期的deadline,然后我們調(diào)用context.WithDeadline(context.Background(), d)得到一個(gè)上下文(ctx)和一個(gè)取消函數(shù)(cancel)喘漏,然后使用一個(gè)select讓主程序陷入等待:等待1秒后打印overslept退出或者等待ctx過期后退出护蝶。 因?yàn)閏tx50毫秒后就過期,所以ctx.Done()會先接收到值翩迈,上面的代碼會打印ctx.Err()取消原因持灰。
func WithTimeout
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithTimeout返回WithDeadline(parent, time.Now().Add(timeout))方法。
取消此Context 將釋放與之關(guān)聯(lián)的資源负饲,因此在此Context 中運(yùn)行的操作完成后堤魁,代碼應(yīng)立即調(diào)用cancel:
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
select {
case <-time.After(1 * time.Second):
fmt.Println("overslept")
case <-ctx.Done():
fmt.Println(ctx.Err()) // prints "context deadline exceeded"
}
}
func WithValue
func WithValue(parent Context, key, val interface{}) Context
WithValue返回父項(xiàng)的副本喂链,其中與key關(guān)聯(lián)的值為val。
僅將Context值用于傳遞流程和API的請求范圍的數(shù)據(jù)妥泉,而不用于將可選參數(shù)傳遞給函數(shù)椭微。
提供的密鑰必須具有可比性,并且不能為字符串類型或任何其他內(nèi)置類型盲链,以避免使用上下文在程序包之間發(fā)生沖突蝇率。 WithValue的用戶應(yīng)定義自己的密鑰類型。 為了避免在分配給接口{}時(shí)進(jìn)行分配刽沾,上下文鍵通常具有具體的類型struct {}本慕。 另外,導(dǎo)出的上下文鍵變量的靜態(tài)類型應(yīng)該是指針或接口侧漓。
func main() {
type favContextKey string
f := func(ctx context.Context, k favContextKey) {
if v := ctx.Value(k); v != nil {
fmt.Println("found value:", v)
return
}
fmt.Println("key not found:", k)
}
k := favContextKey("language")
ctx := context.WithValue(context.Background(), k, "Go")
f(ctx, k)
f(ctx, favContextKey("color"))
}