go等待一組協程結束的操作方式
go提供瞭sync包和channel來解決協程同步和通訊。
方式1:
sync.WaitGroup是等待一組協程結束,sync.WaitGroup隻有3個方法,Add()添加一個計數,Done()減去一個計數,Wait()阻塞直到所有任務完成。
package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup //定義一個同步等待的組 func task(i int){ fmt.Println("task...",i) //耗時操作任務,網絡請求,讀取文件 time.Sleep(time.Second) wg.Done() //減去一個計數 } func main(){ for i:= 0;i<10;i++{ wg.Add(1) //添加一個計數 go task(i) } wg.Wait() //阻塞直到所有任務完成 fmt.Println("over") }
運行結果:
task… 9
task… 4
task… 6
task… 0
task… 7
task… 5
task… 1
task… 2
task… 8
task… 3
over
方式2:
利用緩沖信道channel協程之間通訊,其阻塞等待功能實現等待一組協程結束,不能保證其goroutine按照順序執行
package main import ( "fmt" ) var ch = make(chan int,10) func task(i int){ fmt.Println("task...",i) ch <- i } func main(){ for i:= 0;i<10;i++{ go task(i) } for i:= 0;i<10;i++{ <- ch } fmt.Println("over") }
運行結果:
task… 9
task… 0
task… 1
task… 2
task… 6
task… 7
task… 3
task… 4
task… 8
task… 5
over
方式3:
利用無緩沖的信道channel協程之間通訊,其阻塞等待功能實現等待一組協程結束,保證瞭其goroutine按照順序執行
package main import ( "fmt" "time" ) var ch = make(chan int) func task(i int){ fmt.Println("task...",i) time.Sleep(time.Second) <- ch } func main(){ for i:= 0;i<10;i++{ go task(i) ch <- i } fmt.Println("over") }
運行結果:
task… 0
task… 1
task… 2
task… 3
task… 4
task… 5
task… 6
task… 7
task… 8
task… 9
over
補充:Go中使用Channel等待所有協程結束
讓main方法等待所有協程執行完畢再退出。可能一般思路是設置一個共有變量,然後通過修改這個變量的狀態。這是通過共享變量來通信的方式,而go要做的是,通過通信來共享內存。
1. 按順序執行
每次通信進行成對通信,當main向協程發送一個寫channel時,同時也等待協程返回一個讀channel。
這兩個channel一定是成對的,所以構造一個結構體
type worker struct { in chan int done chan bool } func chanDemo1(){ var workers [10]worker for i := 0; i < 10; i++ { workers[i] = createWorker1(i) } for i := 0; i < 10; i++ { workers[i].in <- 'a' + i <- workers[i].done } for i := 0; i < 10; i++ { workers[i].in <- 'A' + i <- workers[i].done } } func createWorker1(id int) worker { work := worker{ in: make(chan int), done: make(chan bool), } go func() { for { fmt.Printf("Work %d receiverd %c\n", id, <- work.in) work.done <- true } }() return work } func main(){ chanDemo1() fmt.Println("over") }
這個執行結果完全是按照0-9,先小寫再大寫的順序
如果這樣順序執行,還要協程幹啥
2. 批量處理
type worker struct { in chan int done chan bool } func chanDemo1(){ var workers [10]worker for i := 0; i < 10; i++ { workers[i] = createWorker1(i) } for i := 0; i < 10; i++ { workers[i].in <- 'a' + i } for _, worker := range workers { <- worker.done } for i := 0; i < 10; i++ { workers[i].in <- 'A' + i } for _, worker := range workers { <- worker.done } } func createWorker1(id int) worker { work := worker{ in: make(chan int), done: make(chan bool), } go func() { for { fmt.Printf("Work %d receiverd %c\n", id, <- work.in) work.done <- true } }() return work }
這樣的話,先打印小寫,再打印大寫,但是大小寫時順序不固定
3. 完全隨機
func chanDemo1(){ var workers [10]worker for i := 0; i < 10; i++ { workers[i] = createWorker1(i) } for i := 0; i < 10; i++ { workers[i].in <- 'a' + i } for i := 0; i < 10; i++ { workers[i].in <- 'A' + i } for _, worker := range workers { <- worker.done <- worker.done } } func createWorker1(id int) worker { work := worker{ in: make(chan int), done: make(chan bool), } go func() { for { fmt.Printf("Work %d receiverd %c\n", id, <- work.in) // 再開一個協程 go func() { work.done <- true}() } }() return work }
這種方式就是完全隨機瞭
使用channel進行樹的遍歷
func (node *Node) TraverseFunc(f func(*Node)){ if node == nil{ return } node.Left.TraverseFunc(f) f(node) node.Right.TraverseFunc(f) } func (node *Node) TraverseWithChannel() chan *Node{ out := make(chan *Node) go func() { node.TraverseFunc(func(node *Node) { out <- node }) close(out) }() return out } func main(){ var root Node root = Node{Value:3} root.Left = &Node{} root.Right = &Node{5,nil,nil} root.Right.Left = new(Node) root.Left.Right =&Node{6,nil,nil} root.Traverse() c:=root.TraverseWithChannel() maxNode := 0 for node := range c{ if node.Value > maxNode{ maxNode = node.Value } } fmt.Println("max node value:", maxNode)
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。
推薦閱讀:
- Go緩沖channel和非緩沖channel的區別說明
- Go語言通道之緩沖通道
- Go語言入門學習之Channel通道詳解
- Go語言如何輕松編寫高效可靠的並發程序
- Golang 內存模型The Go Memory Model