context
包是Go 語言中用來設(shè)置截止日期慕购、同步信號震桶,傳遞請求相關(guān)值的結(jié)構(gòu)體,是開發(fā)常用的并發(fā)控制技術(shù)毫蚓。
與WaitGroup
的不同在于context
可以控制多級的goroutine
占键。
1. 接口定義
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <- chan struct{}
Err() error
Value(key interface{}) interface{}
}
Deadline(): 工作的截止時間,沒有設(shè)置
deadline
則ok==false
元潘。
Done(): 需要在select-case
語句中使用(case <-context.Done():
)畔乙。當(dāng)context
被關(guān)閉后,Done()
返回一個被關(guān)閉的通道(關(guān)閉的通道依然是可以讀的翩概,所以goroutine
可以收到關(guān)閉請求)牲距;當(dāng)context
還未關(guān)閉時,Done()
返回nil
钥庇。
Err(): 描述context
關(guān)閉的原因牍鞠,其原因由context
實現(xiàn)控制。例如:因deadline
關(guān)閉:context deadline exceeded
评姨;因主動關(guān)閉:context canceled
难述。沒有關(guān)閉時,返回nil
吐句。
Value(): 特別的用于一種context
:不用于控制呈樹狀分布的goroutine
胁后,而是用于在樹狀分布的goroutine
之間傳遞信息。Value()
方法根據(jù)key
值查詢map
中的Value
嗦枢。
2. 使用
一個案例來展示context
的使用攀芯,它做了2件事:1. 創(chuàng)建過期時間為1s的上下文。 2. 將context
傳入handle
函數(shù)中净宵,函數(shù)使用500ms的時間處理請求敲才。
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
go handle(ctx, 500*time.Millisecond)
select {
case <-ctx.Done():
fmt.Println("main", ctx.Err())
}
}
func handle(ctx context.Context, duration time.Duration) {
select {
case <-ctx.Done():
fmt.Println("handle", ctx.Err())
case <-time.After(duration):
fmt.Println("process request with", duration)
}
}
output:
process request with 500ms
main context deadline exceeded
分析: context
過期時間為1s裹纳,處理時間為0.5秒(select
中的過期時間),函數(shù)有足夠的時間完成處理紧武,也就是<-time.After(duration):
會在<-ctx.Done()
之前完成剃氧,故輸出process request with 500ms
。再過0.5s阻星,<-ctx.Done()
完成朋鞍,這時候輸出main context deadline exceeded
。
倘若妥箕,代碼中的``改為400*time.Millisecond
滥酥,會輸出什么呢?
A:
main context deadline exceeded
B:
main context deadline exceeded
handle context deadline exceeded
C:
process request with 500ms
main context deadline exceeded
D:
process request with 500ms
E:
handle context deadline exceeded
main context deadline exceeded
答案是:A畦幢、B坎吻、E
可能出現(xiàn)這3種,而不是1種的原因是和調(diào)度器有關(guān)宇葱。
context
的一些方法:
-
默認(rèn)上下文: 以下兩個方法都會返回預(yù)先初始化好的私有變量
background
和todo
瘦真,它們會在同一個 Go 程序中被復(fù)用。這兩個私有變量都是通過new(emptyCtx)
語句初始化的黍瞧,它們是指向私有結(jié)構(gòu)體context.emptyCtx
的指針诸尽,這是最簡單、最常用的上下文類型印颤。-
context.Background()
: 是上下文的默認(rèn)值您机,所有其他的上下文都應(yīng)該從它衍生出來。 -
context.TODO()
: 應(yīng)該僅在不確定應(yīng)該使用哪種上下文時使用年局。
-
-
取消信號:前兩個創(chuàng)建的是
context.WithCancel
际看,最后一個創(chuàng)建的是context.timerCtx
。-
context.WithCancel()
: 由context.Context()
衍生出的特殊的子上下文某宪。一旦它的返回函數(shù)被執(zhí)行仿村,其所有子context
將都會被返回。 -
context.WithDeadline()
: 在某個時間點進(jìn)行返回兴喂。 -
context.WithTimeout()
: 某個時間段過后進(jìn)行返回蔼囊。
-
-
傳值方法:
-
context.WithValue()
: 從父上下文中創(chuàng)建一個子上下文,傳值的子上下文使用context.valueCtx
衣迷。
需要注意的是這個方法是遞歸的根據(jù)Key
來獲取Value
的畏鼓。
-
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}