go語言中goroutine之間的關聯關係,缺少維護,在erlang中有專門的機制來保障新開協程的生命週期,在go語言中,只能經過channel + select來實現,但不夠直觀,很繞。
Context 一般被譯做 上下文 ,它是一個比較抽象的概念,通常理解爲程序單元的一個運行狀態、現場、快照。上下上下則是存在上下層的傳遞, 上 會把內容傳遞給 下 。
在Go語言中,程序單元也就指的是Goroutine。context 包不只實現了在程序單元之間共享狀態變量的方法,同時能經過簡單的方法,使咱們在被調用程序單元的外部,經過設置ctx變量值,將過時或撤銷信號傳遞給被調用的程序單元。html
在go服務器中(http服務器),對於每一個請求的request都是在單獨的goroutine中進行的,處理一個request也可能涉及多個goroutine之間的交互, 使用context可使開發者方便地在這些goroutine裏傳遞request相關的數據、取消goroutine的signal或截止時間golang
Done 方法在Context被取消或超時時返回一個close的channel,close的channel能夠做爲廣播通知,告訴給context相關的函數要中止當前工做而後返回。安全
當一個父operation啓動一個goroutine用於子operation,這些子operation不可以取消父operation。下面描述的WithCancel函數提供一種方式能夠取消新建立的Context.服務器
Context能夠安全的被多個goroutine使用。開發者能夠把一個Context傳遞給任意多個goroutine而後cancel這個context的時候就可以通知到全部的goroutine。ide
Err方法返回context爲何被取消。函數
Deadline返回context什麼時候會超時。協程
Value返回context相關的數據。htm
須要注意的就是 調用CancelFunc會取消child以及child生成的context,取出父context對這個child的引用,中止相關的計數器blog
實戰:生命週期
參考示例1:
package main import ( "context" "log" "os" "time" ) var logg *log.Logger func someHandler() { ctx, cancel := context.WithCancel(context.Background()) go doStuff(ctx) //10秒後取消doStuff time.Sleep(10 * time.Second) cancel() } //每1秒work一下,同時會判斷ctx是否被取消了,若是是就退出 func doStuff(ctx context.Context) { for { time.Sleep(1 * time.Second) select { case <-ctx.Done(): logg.Printf("done") return default: logg.Printf("work") } } } func main() { logg = log.New(os.Stdout, "", log.Ltime) someHandler() logg.Printf("down") time.Sleep(10 * time.Second) }
輸出
18:00:17 work 18:00:18 work 18:00:19 work 18:00:20 work 18:00:21 work 18:00:22 work 18:00:23 work 18:00:24 work 18:00:25 work 18:00:26 down 18:00:26 done
參考示例2:
package main import ( "context" "log" "os" "time" ) var logg *log.Logger func doTimeOutStuff(ctx context.Context) { for { time.Sleep(1 * time.Second) if deadline, ok := ctx.Deadline(); ok { //設置了deadl logg.Printf("deadline set") if time.Now().After(deadline) { logg.Printf(ctx.Err().Error()) return } } select { case <-ctx.Done(): logg.Printf("done") return default: logg.Printf("work") } } } func timeoutHandler() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) go doTimeOutStuff(ctx) // go doStuff(ctx) time.Sleep(10 * time.Second) cancel() } func main() { logg = log.New(os.Stdout, "", log.Ltime) timeoutHandler() logg.Printf("down") time.Sleep(10 * time.Second) }
輸出:
18:04:37 deadline set 18:04:37 work 18:04:38 deadline set 18:04:38 work 18:04:39 deadline set 18:04:39 work 18:04:40 deadline set 18:04:40 work 18:04:41 deadline set 18:04:41 context deadline exceeded 18:04:46 down
參考連接:
https://studygolang.com/articles/12566
https://www.cnblogs.com/zhangboyu/p/7456606.html
https://studygolang.com/articles/13866?fr=sidebar