控制併發有兩種經典的方式,一種是WaitGroup,另一種就是Context安全
func main() { var wg sync.WaitGroup wg.Add(2) go func() { time.Sleep(2*time.Second) fmt.Println("1號完成") wg.Done() }() go func() { time.Sleep(2*time.Second) fmt.Println("2號完成") wg.Done() }() wg.Wait() fmt.Println("好了,你們都幹完了,放工") }
以上例子必定要等到兩個goroutine同時作完纔會所有完成,這種控制併發方式尤爲適用於多個goroutine協同作一件事情的時候。
func main() { stop := make(chan bool) go func() { for { select { case <-stop: fmt.Println("監控退出,中止了...") return default: fmt.Println("goroutine監控中...") time.Sleep(2 * time.Second) } } }() time.Sleep(10 * time.Second) fmt.Println("能夠了,通知監控中止") stop<- true //爲了檢測監控過是否中止,若是沒有監控輸出,就表示中止了 time.Sleep(5 * time.Second) }
例子中咱們經過select判斷stop是否接受到值,若是接受到值就表示能夠推出中止了,若是沒有接受到,就會執行default裏面的監控邏輯,繼續監控,直到收到stop的通知 以上控制goroutine的方式在大多數狀況下能夠知足咱們的使用,可是也存在不少侷限性,好比有不少goroutiine,而且這些goroutine還衍生了其餘goroutine,此時chan就比較困難解決這樣的問題了
以上問題是存在的,好比一個網絡請求request,每一個request都須要開啓一個goroutine作一些事情。因此咱們須要一種能夠跟蹤goroutine的方案才能夠達到控制的目的,go爲咱們提供了Context網絡
func main() { ctx, cancel := context.WithCancel(context.Background()) go watch(ctx,"【監控1】") go watch(ctx,"【監控2】") go watch(ctx,"【監控3】") time.Sleep(10 * time.Second) fmt.Println("能夠了,通知監控中止") cancel() //爲了檢測監控過是否中止,若是沒有監控輸出,就表示中止了 time.Sleep(5 * time.Second) } func watch(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Println(name,"監控退出,中止了...") return default: fmt.Println(name,"goroutine監控中...") time.Sleep(2 * time.Second) } } }
例子中啓動了3個監控goroutine進行不斷的監控,每個都使用Context進行跟蹤,當咱們使用cancel函數通知取消時候,這3個 goroutine都會被結束。全部基於這個context或者衍生出來的子Context都會收到通知,這樣就能夠進行清理操做最終釋放goroutine了併發
Context是一個接口,具體的內容以下:
~go~函數
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithValue(parent Context, key, val interface{}) Context
這四個With函數,接收的都有一個partent參數,就是父Context,咱們要基於這個父Context建立出子Context的意思線程