Goroutine 之間經常使用的通訊方式有:編程
- 全局變量
- channel
- context 上下文
sync.WaitGroup
若是隻是單純的等待全部任務完成,能夠使用 sync.WaitGroup
:併發
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup for i := 0; i < 3; i++ { wg.Add(1) go func() { fmt.Println("func run") time.Sleep(time.Second) wg.Done() }() } wg.Wait() fmt.Println("main done") }
全局變量
簡單,可是傳遞的數據只能一寫多讀。spa
package main import ( "fmt" "time" ) var stop bool func main() { go f() time.Sleep(2 * time.Second) stop = false time.Sleep(2 * time.Second) fmt.Println("main done") } func f() { for stop { fmt.Println("still run") time.Sleep(time.Second) } }
channel
CSP 併發編程模型(Communicating Sequential Process)。channel 在 Golang 中是核心類型。code
Golang 的 select 機制在語言層面實現了相似 Linux 的 select 功能,能夠監聽多個文件描述符的讀寫事件,可以在事件發生時主動通知應用程序處理。 Golang 的 select 還能夠設置 default,在監聽的事件所有阻塞時執行。事件
package main import ( "fmt" "time" ) func main() { stop := make(chan bool) go f(stop) time.Sleep(2 * time.Second) stop<-true time.Sleep(2 * time.Second) fmt.Println("main done") } func f(stop chan bool) { for { select { case <-stop: fmt.Println("done") return default: fmt.Println("still run") time.Sleep(time.Second) } } }
context 上下文
Golang 的上下文是樹狀結構,經過 context.Background()
方法能夠拿到上下文的根節點。經常使用方法有:string
- 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
WithCancel
子上下文能夠調用 Done 方法(返回 channel,能夠經過 select 讀取)檢測是否有父節點調用 cancel。上層節點的 cancel 調用會沿着上下文樹的邊向下通知到每個子節點。it
package main import ( "fmt" "time" "context" ) func main() { ctx, myCancel := context.WithCancel(context.Background()) go func() { for { select { case <-ctx.Done(): fmt.Println("ctx Done") return default: fmt.Println("goroutine continue") time.Sleep(time.Second) } } }() time.Sleep(time.Second * 2) myCancel() time.Sleep(time.Second) fmt.Println("main done") }
固然,這裏能夠使用 sync.WaitGroup
來避免睡眠一段時間:io
package main import ( "fmt" "time" "context" "sync" ) func main() { var wg sync.WaitGroup wg.Add(1) ctx, myCancel := context.WithCancel(context.Background()) go func() { for { select { case <-ctx.Done(): fmt.Println("ctx Done") wg.Done() return default: fmt.Println("goroutine continue") time.Sleep(time.Second) } } }() time.Sleep(time.Second * 2) myCancel() wg.Wait() fmt.Println("main done") }
WithValue
package main import ( "fmt" "context" ) type favContextKey string func main() { ctx := context.WithValue(context.Background(), favContextKey("hello"), "Go") f := func(ctx context.Context, k favContextKey) { if v := ctx.Value(k); v != nil { fmt.Println("found value:", v) return } fmt.Println("value not found:", k) } f(ctx, favContextKey("hello")) f(ctx, favContextKey("color")) }