golang協程的使用-2018-11-06
1. 建立一個協程
一般直接在要執行的函式前,加上 go 就是啟用了一個執行該函式的協程。
比如下面的小例子,啟動say函式的協程。
package main import ( "fmt" "time" ) func say(s string) { fmt.Println(s) } func main() { for i := 0; i < 5; i++ { go say("Hello") } }
這樣已經是在main函式中啟動了一個協程,去執行say函式。但是結果卻什麼都沒打印出來。
後來理解了一下,go啟動了一個協程,啟動之後,系統不會阻塞,程式可以繼續執行後面的流程。這個go協程啟動之後,後面直接到了main函式的結束的地方,所以main函式直接結束了,但是協程啟動的函式還沒來得及執行,所以沒有任何資訊輸出。
可以再main函式結束前,加1s的等待,這樣在等待的過程中,協程啟動的函式也就執行完輸出了結果,最終main函式結束。
func main() { for i := 0; i < 5; i++ { go say("Hello") } time.Sleep(time.Second * 1) }
這也是golang中協程使用需要注意的一個點吧。啟動了之後,要注意被啟動的函式的正常執行,當所有協程執行完成後,才讓main函式退出。
需要
配合channel
或者waitgroup
去使用協程,阻塞主程式的退出。
2. 配合waitGroup啟動協程
- 定義一個WaitGroup物件wg。注意這裡是一個全域性的變數,使用時都是這個變數的內容,所以不需要定義為指標型別,否則這樣宣告會是個空指標。
- wg.Add(n),n是正整數是說有幾個協程要處理
- wg.Done(),他就相當於wg.Add(-1),執行完一次go協程的函式,可以啟動一次wg.Done(),n也就-1了。
- wg.Wait(),是告訴主函式去等一等,等到全部的協程執行完,也就是最終總的n為0的時候,所有的協程都處理完了,main函式結束。
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func say(s string) { defer wg.Done() fmt.Println(s) } func main() { for i := 0; i < 5; i++ { wg.Add(1) go say("Hello") } wg.Wait() }
3.配合channel使用協程
上面使用了waitgroup,可以滿足所有的協程執行完後,在退出主程式。我們還要學會配合channel去使用協程。
因為我們剛才是直接列印的協程啟動的函式裡的資料,但是並沒有實現資料的傳輸。channel就是處理協程的資料傳輸的通道了。
關於channel的用法以後可以再其他文章中補充,這裡就直接看怎麼能結合goroutine了。
- 初始化一個channel,有緩衝
- 啟動協程時,在函式中通過channel寫入傳的引數。
- 讀取channel中的資料
package main import ( "fmt" "sync" ) var wg sync.WaitGroup var ch = make(chan int, 5) func say(s int) { defer wg.Done() ch <- s //將s寫入ch fmt.Println("寫入:", s) } func main() { for i := 0; i < 5; i++ { wg.Add(1) go say(i) } wg.Wait() //所有協程執行完了,資料都寫入了 close(ch) //關閉ch for v := range ch { //遍歷出ch中的值 fmt.Println("v", v) } }
當然,channel的使用還有很多坑,也需要注意,以後遇到記錄