什么是context
從go1.7開始细移,golang.org/x/net/context包正式作為context包進(jìn)入了標(biāo)準(zhǔn)庫楞黄。那么宛蚓,這個包到底是做什么的呢斜姥?根據(jù)官方的文檔說明:
Package context defines the Context type, which carries deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.
也就是說洲押,通過context武花,我們可以方便地對同一個請求所產(chǎn)生地goroutine進(jìn)行約束管理,可以設(shè)定超時杈帐、deadline体箕,甚至是取消這個請求相關(guān)的所有g(shù)oroutine。形象地說挑童,假如一個請求過來累铅,需要A去做事情,而A讓B去做一些事情站叼,B讓C去做一些事情娃兽,A、B尽楔、C是三個有關(guān)聯(lián)的goroutine投储,那么問題來了:假如在A、B阔馋、C還在處理事情的時候請求被取消了玛荞,那么該如何優(yōu)雅地同時關(guān)閉goroutine A、B呕寝、C呢勋眯?這個時候就輪到context包上場了。
如何使用context
在開始說明如何使用context之前下梢,先來看看context有什么相關(guān)的方法定義客蹋。
先來看看context的代碼示例:
package main
import (
"context"
"log"
"net/http"
_ "net/http/pprof"
"time"
)
func main() {
go http.ListenAndServe(":8989", nil)
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(3 * time.Second)
cancel()
}()
log.Println(A(ctx))
select {}
}
func C(ctx context.Context) string {
select {
case <-ctx.Done():
return "C Done"
}
return ""
}
func B(ctx context.Context) string {
ctx, _ = context.WithCancel(ctx)
go log.Println(C(ctx))
select {
case <-ctx.Done():
return "B Done"
}
return ""
}
func A(ctx context.Context) string {
go log.Println(B(ctx))
select {
case <-ctx.Done():
return "A Done"
}
return ""
}
運行結(jié)果:
2016/12/25 22:27:00 A Done
2016/12/25 22:27:00 C Done
2016/12/25 22:27:00 B Done
pprof截圖,剛開始的時候:
A孽江、B讶坯、C結(jié)束后:
在這里,我們用http的pprof來查看運行時有多少個goroutine正在執(zhí)行竟坛,
go http.ListenAndServe(":8989", nil)
這一句是啟動一個http服務(wù)器闽巩,用來查看程序的一些運行信息。
我們用context.Background()
來實例化一個context担汤,然后調(diào)用WithCancel()
方法來返回一個context和一個取消的方法涎跨,并在3秒后調(diào)用這個cancel方法關(guān)閉goroutine A、B崭歧、C隅很。從程序的運行結(jié)果看,我們調(diào)用了一次cancel方法,子goroutine的ctx.Done()
都收到了關(guān)閉信號叔营。從pprof的截圖也可以看到屋彪,從一開始有5個goroutine,到關(guān)閉后剩下兩個绒尊,也就是A畜挥、B、C三個goroutine都已經(jīng)關(guān)閉了婴谱。
這里的例子是直接調(diào)用了context.WithCancel()
蟹但,我們也可以使用context.WithTimeout()
和context.WithDeadline()
來設(shè)置goroutine的超時時間和最終的運行時間。具體的用法可以看一下官方文檔谭羔,這里就不細(xì)說了华糖。另外有一個方法在例子中沒有用到,那就是context.WithValue()
瘟裸。這個方法是用來傳遞在這次的請求處理中相關(guān)goroutine的共享變量客叉,這與全局變量是有所區(qū)別的,因為它只在這次的請求范圍內(nèi)有效话告。
context的使用規(guī)范
在最新的1.8 beta版本中兼搏,很多相關(guān)的包都加入了context,比如database包沙郭。那么向族,在使用context的時候有哪些需要注意呢?
- 不要把context存儲在結(jié)構(gòu)體中棠绘,而是要顯式地進(jìn)行傳遞
- 把context作為第一個參數(shù),并且一般都把變量命名為ctx
- 就算是程序允許再扭,也不要傳入一個nil的context氧苍,如果不知道是否要用context的話,用
context.TODO()
來替代 -
context.WithValue()
只用來傳遞請求范圍的值泛范,不要用它來傳遞可選參數(shù) - 就算是被多個不同的goroutine使用让虐,context也是安全的
context的初步了解暫時就先說這么多了,以后有空可以研究一下源碼看看context是如何實現(xiàn)的罢荡。
圣誕快樂赡突!