這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
聲明
轉載請註明本文地址,感謝 :)
瞭解進程、線程、協程
本文不詳細解釋這三個名詞的意思,下面有一篇文章,不懂的同學可以參考看看。
- http://www.cnblogs.com/work115/p/5620272.html
Goroutine的使用
- Goroutine 奉行通過通訊來共用記憶體,而不是共用記憶體來通訊
- 使用goroutine很簡單,只需要用到一個關鍵字go,我們用一段代碼來樣本一下如何使用go關鍵字
12345678910111213 |
package mainimport ("fmt")func main() {go Goroutine()}func Goroutine() {fmt.Println("Goroutine")} |
沒有輸出?
123456789101112131415 |
package mainimport ("fmt""time")func main() {go Goroutine()time.Sleep(3 * time.Second)}func Goroutine() {fmt.Println("Goroutine")} |
- 不出所料,程式成功列印出了“Goroutine”字串。
引入Channel
- 通過上面的實驗,我們成功地用Goroutine,並列印出了一句話。現在問題來了,這個並不是我們想要的。為什嗎?如果我的Goroutine函數裡的代碼需要執行10s、20s甚至更多的時候,怎麼辦?難道還是繼續Sleep更多的秒數嗎?
- 我們需要的是,當Goroutine函數執行完畢的時候,自動通知main函數。所以這裡我們就引出了channel
建立方式
12345678910111213141516 |
package mainimport ("fmt")func main() {chann := make(chan bool)go func() {fmt.Println("Goroutine")chann <- true}()<- chann} |
- 如果運行以上的程式碼片段後,你成功輸出了Goroutine,那麼說明我們的寫法是沒錯的。現在我們來理解一下這段代碼的意思,我們看看匿名函數外面的
<-chann,這實際上是起到了阻塞的作用,當匿名函數裡面的業務沒有執行完並講true傳入channel中的時候,這個程式是不會退出的,匿名函數外面會一直在等待。當匿名函數裡面執行到了chann <- true,也就是將true傳入了channel的時候,就不會繼續阻塞,這時候程式就會結束。這就是channel的作用。
使用for range迭代輸出Channel
- 需要注意的是,當你在對channel 迭代的時候,必須在某個位置明確的關閉這個channel,不然程式就會死結。我們用一個程式碼片段來樣本一下:
1234567891011121314151617181920 |
package mainimport ("fmt")func main() {chann := make(chan bool)go func() {fmt.Println("Goroutine")chann <- trueclose(chann) //關閉channel}()for v := range chann {fmt.Println(v)}} |
Channel 的定義類型
- Channel 類型的定義格式:
ChannelType = ("chan" | "chan" "<-" | "<-" "chan")
- Channel包含了三種類型的定義,可選的<-代表的是Channel的方向。沒有指定方向的話,Channel就是雙向的,既可以接受資料,也可以發送資料。
chan T //可以接收也可以發送
chan <- bool //可以發送bool類型的資料
<- chan bool //可以接收int類型的資料
無緩衝Channel與緩衝Channel
無緩衝Channel
- 定義方式
chann := make(chan int)
- 所謂無緩衝Channel就是在通道中只能傳入1個值,若是傳入的值一直沒有被取走,那麼就會一直阻塞著,直到被取走。可以說是同步阻塞的
- 需要注意的是,無緩衝Channel一定是先取後傳。當代碼中出現了取的操作時,發現Channel裡面並沒有值,那麼就會發生阻塞,當Channel裡有值了,才會停止阻塞。
緩衝Channel
- 定義方式
chann := make(chan int,100)
- 有緩衝Channel,比如緩衝值是100,那麼除非傳入的值已經達到了100了,否則這個Channel中,還是可以不斷地傳值進去。當Channel滿了,就會發生阻塞,等值被取走後,才可以繼續傳值。
如何判斷一組任務是否完成
使用緩衝Channel
12345678910111213141516171819202122232425262728 |
package mainimport ("fmt""runtime")func main() {runtime.GOMAXPROCS(runtime.NumCPU()) // 設定線程數為當前電腦的CPU數chann := make(chan bool, 10)for i := 0; i < 10; i++ {go Go(chann, i)}for i := 0; i < 10; i++ {<-chann}}func Go(chann chan bool, index int) {a := 1for i := 0; i < 100000000; i++ {a += 1}fmt.Println(index, a)chann <- true} |
使用sync包
- 通過設定一個工作群組,大小為10。每完成一個任務就記錄一次
wg.Done(),當10個任務都完成的時候,程式自動結束。
1234567891011121314151617181920212223242526272829 |
package mainimport ("fmt""runtime""sync")func main() {runtime.GOMAXPROCS(runtime.NumCPU())wg :=sync.WaitGroup{}wg.Add(10)for i := 0; i < 10; i++ {go Go(&wg, i)}wg.Wait()}func Go(wg *sync.WaitGroup, index int) {a := 1for i := 0; i < 100000000; i++ {a += 1}fmt.Println(index, a)wg.Done()} |
Select
select 用於多個channel監聽並收發訊息,當任何一個case滿足條件則會執行,若沒有可執行檔case,就會執行default,如果沒有default,程式就會阻塞。
12345678910111213141516171819202122232425262728293031 |
package mainimport ("fmt""time")func main() {chann := make(chan int)go enqueue(chann)for {select {case v, ok := <-chann:if ok {fmt.Println(v)} else {fmt.Println("close")return}default:fmt.Println("waiting")}}}func enqueue(chann chan int) {time.Sleep(3 * time.Second)chann <- 1close(chann)} |
參考
- http://blog.csdn.net/gaox587/article/details/53005575
- http://blog.csdn.net/shachao888/article/details/52944212
- https://github.com/Unknwon/go-fundamental-programming/blob/master/lectures/lecture14.md