一個請求,可能涉及多個API調用,多個goroutine,如何在多個API 之間,以及多個goroutine之間協做和傳遞信息,就是一個問題。golang
好比一個網絡請求Request,須要開啓一些goroutine去訪問後端資源(好比,數據庫,RPC服務等),這些goroutine又可能會開啓其餘的goroutine,如何跟蹤和控制這些goroutine呢?數據庫
golang定義了 context包,用於解決這個問題。後端
context
能夠在多個API和進程之間,實現deadlines
截止日期操做、cancelation signals
取消操做以及傳遞request-scoped
的值(即傳遞參數)。網絡
context
一個最大的特色是能夠繼承。父context,能夠派生子context,子context有又能夠派生子context。若是父context被取消,子context及其子context都會被取消。併發
經常使用派生的方法有:WithCancel
WithDeadline
WithTimeout
WithValue
.
其中,函數
WithCancel
用於主動取消自身以及子context。WithDeadline
和WithTimeout
用於超時狀況下的取消自身以及子context。WithTimeout
實現上直接調用WithDeadline
。WithValue
用於context
傳遞request-scoped
的參數,而不是向函數傳遞的可選參數。下面例子演示如何控制多個goroutine的退出。post
首先,啓動3個goroutine,分別命名爲"1"、"2"、"3",每一個goroutine又啓動一個goroutine。也就是有6個goroutine。它們之間的關係以下:.net
最後,在main
中調用取消操做cancel
,先是1,2,3收到信號退出,1,2,3退出的時候又調用它們的cancel
,這樣全部的goroutine都被取消並退出。code
具體代碼以下:blog
package main import ( "context" "fmt" "time" ) func PrintTask(ctx context.Context, taskName string) { for { select { case <- ctx.Done(): fmt.Println("task:", taskName, " exit...") return default: time.Sleep(1*time.Second) fmt.Println("task:", taskName, " doing something...") } } } func mainTask(ctx context.Context, taskName string) { ctx1, cancel := context.WithCancel(ctx) defer cancel() // create a new task newTaskName := taskName + "1" go PrintTask(ctx1, newTaskName) for { select { case <- ctx.Done(): fmt.Println("task:", taskName, " exit...") return default: time.Sleep(1*time.Second) fmt.Println("task:", taskName, " doing something...") } } } func main() { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) go mainTask(ctx, "1") go mainTask(ctx, "2") go mainTask(ctx, "3") time.Sleep(3*time.Second) cancel() fmt.Println("main exit...") time.Sleep(3*time.Second) }
輸出
task: 1 doing something... task: 21 doing something... task: 11 doing something... task: 3 doing something... task: 2 doing something... task: 31 doing something... task: 3 doing something... task: 2 doing something... task: 11 doing something... task: 21 doing something... task: 31 doing something... task: 1 doing something... task: 11 doing something... task: 3 doing something... task: 1 doing something... task: 21 doing something... task: 2 doing something... task: 31 doing something... main exit... task: 11 doing something... task: 11 exit... task: 21 doing something... task: 21 exit... task: 3 doing something... task: 3 exit... task: 31 doing something... task: 31 exit... task: 1 doing something... task: 1 exit... task: 2 doing something... task: 2 exit...