context包是Go 語言中用來設置截止日期、同步信號,傳遞請求相關值的結構體,是開發(fā)常用的并發(fā)控制技術。
與WaitGroup的不同在于context可以控制多級的goroutine。
1. 接口定義
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <- chan struct{}
Err() error
Value(key interface{}) interface{}
}
Deadline(): 工作的截止時間,沒有設置
deadline則ok==false。
Done(): 需要在select-case語句中使用(case <-context.Done():)。當context被關閉后,Done()返回一個被關閉的通道(關閉的通道依然是可以讀的,所以goroutine可以收到關閉請求);當context還未關閉時,Done()返回nil。
Err(): 描述context關閉的原因,其原因由context實現(xiàn)控制。例如:因deadline關閉:context deadline exceeded;因主動關閉:context canceled。沒有關閉時,返回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)度器有關。
context的一些方法:
-
默認上下文: 以下兩個方法都會返回預先初始化好的私有變量
background和todo,它們會在同一個 Go 程序中被復用。這兩個私有變量都是通過new(emptyCtx)語句初始化的,它們是指向私有結構體context.emptyCtx的指針,這是最簡單、最常用的上下文類型。-
context.Background(): 是上下文的默認值,所有其他的上下文都應該從它衍生出來。 -
context.TODO(): 應該僅在不確定應該使用哪種上下文時使用。
-
-
取消信號:前兩個創(chuàng)建的是
context.WithCancel,最后一個創(chuàng)建的是context.timerCtx。-
context.WithCancel(): 由context.Context()衍生出的特殊的子上下文。一旦它的返回函數(shù)被執(zhí)行,其所有子context將都會被返回。 -
context.WithDeadline(): 在某個時間點進行返回。 -
context.WithTimeout(): 某個時間段過后進行返回。
-
-
傳值方法:
-
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)
}