淺談GoLang協程

來源:互聯網
上載者:User

GoLang協程

學習golang也有一段時間了,這裡講一下自己對golang協程的使用理解,golang很多人都知道,畢竟有個好爹Google,提起golang和其它語言最大區別莫過於goroutine,
也就是go的協程,先來一個demo

package mainfunc say(s string) {    for i := 0; i < 5; i++ {        println(s)    }}func main() {    go say("Hello")    go say("World")}

go 啟動協程的方式就是使用關鍵字 go,後面一般接一個函數或者類似下面的匿名函數的寫法

go func() {    for i := 0; i < 5; i++ {        println(i)    }}()

當然如果你運行上面第一段代碼,你會發現什麼結果都沒有,what???

這至少說明你代碼寫的沒問題,當你使用go啟動協程之後,這2個函數就被切換到協程裡面執行了,但是這時候主線程結束了,這2個協程還沒來得及執行就掛了!
聰明的小夥伴會想到,那我主線程先睡眠1s等一等?Yes, 在main代碼塊最後一行加入:

time.Sleep(time.Second*1) # 表示睡眠1s

你會發現可以列印出5個Hello 和 5個World,多次運行你會發現Hello 和 World 的順序不是固定的,這進一步說明了一個問題,那就是多個協程是同時執行的
不過睡眠這種做法肯定是不靠譜的,go 內建一個WaitGroup可以解決這個問題, 代碼如下:

package mainimport (    "sync")var wg sync.WaitGroupfunc say(s string) {    for i := 0; i < 5; i++ {        println(s)    }    wg.Done()}func main() {    wg.Add(2)        go say("Hello")    go say("World")        wg.Wait()}

簡單說明一下用法,var 是聲明了一個全域變數 wg,類型是sync.WaitGroup,wg.add(2) 是說我有2個goroutine需要執行,
wg.Done 相當於 wg.Add(-1) 意思就是我這個協程執行完了。wg.Wait() 就是告訴主線程要等一下,等他們2個都執行完再退出。

舉個例子,你有一個需求是從3個庫取不同的資料匯總處理,同步代碼的寫法就是查3次庫,但是這3次查詢必須按順序執行,大部分程式設計語言的代碼執行順序都是從上到下,假如一個
查詢耗時1s,3個查詢就是3s,但是使用協程你可以讓這3個查詢同時進行,也就是1s就可以搞定(前提是資料庫跟得上)。還有一個更有實際用途的例子就是用來寫爬蟲。

不過為了更好的使用協程,你可能還得瞭解一下管道 Chanel,go 裡面的管道是協程之間通訊的渠道,上面的例子裡面我們是直接列印出來結果,假如現在的需求是把輸出結果返回到主線程呢?

package mainimport (    "sync")var wg sync.WaitGroupfunc say(s string, c chan string) {    for i := 0; i < 5; i++ {        c <- s    }    wg.Done()}func main() {    wg.Add(2)    ch := make(chan string) // 執行個體化一個管道    go say("Hello", ch)    go say("World", ch)    for {        println(<-ch) //迴圈從管道取資料    }    wg.Wait()}

簡單說明一下,這裡就是執行個體化了一個管道,go啟動的協程同時向這個2個管道輸出資料,主線程使用了一個for迴圈從管道裡面取資料,其實就是一個生產者和消費者模式,和redis隊列有點像

值得一說的是 World 和 Hello 進入管道的順序是不固定的,可能大家實驗的時候發現好像是固定的,那是因為電腦跑的太快了,你把迴圈資料放大,或者在裡面加個睡眠再看看

但是這個程式是有bug的,在程式的啟動並執行最後會輸出這樣的結果:

fatal error: all goroutines are asleep - deadlock! 

報錯資訊的提示意思是所有的協程都睡眠了,程式監測到死結!為什麼會這樣呢?我是這樣理解的,go的管道預設是阻塞的(假如你不設定緩衝的話),你那邊放一個,我這頭才能取一個,
如果你那邊放了東西這邊沒人取,程式就會一直等下去,死結了,同時,如果那邊沒人放東西,你這邊取也取不到,也會發生死結!

如何解決這個問題呢?標準的做法是主動關閉管道,或者你知道你應該什麼時候關閉管道, 當然你結束程式管道自然也會關掉!針對上面的示範代碼,可以這樣寫:

i := 1for {    str := <- ch    println(str)    if i >= 10{        close(ch)        break    }    i++}

因為我們明確知道總共會輸出10個單詞,所以這裡簡單做了一個判斷,大於10就關閉管道退出for迴圈,就不會報錯了!下面是一個利用select從管道取資料的例子:

package mainimport (    "strconv"    "fmt"    "time")func main() {    ch1 := make(chan int)    ch2 := make(chan string)    go pump1(ch1)    go pump2(ch2)    go suck(ch1, ch2)    time.Sleep(time.Duration(time.Second*30))}func pump1(ch chan int) {    for i := 0; ; i++ {        ch <- i * 2        time.Sleep(time.Duration(time.Second))    }}func pump2(ch chan string) {    for i := 0; ; i++ {        ch <- strconv.Itoa(i+5)        time.Sleep(time.Duration(time.Second))    }}func suck(ch1 chan int, ch2 chan string) {    chRate := time.Tick(time.Duration(time.Second*5)) // 定時器    for {        select {        case v := <-ch1:            fmt.Printf("Received on channel 1: %d\n", v)        case v := <-ch2:            fmt.Printf("Received on channel 2: %s\n", v)        case <-chRate:            fmt.Printf("Log log...\n")        }    }}

輸出結果如下:

Received on channel 1: 0Received on channel 2: 5Received on channel 2: 6Received on channel 1: 2Received on channel 1: 4Received on channel 2: 7Received on channel 1: 6Received on channel 2: 8Received on channel 2: 9Received on channel 1: 8Log log...Received on channel 2: 10Received on channel 1: 10Received on channel 1: 12Received on channel 2: 11Received on channel 2: 12Received on channel 1: 14

這個程式建立了2個管道一個傳輸int,一個傳輸string,同時啟動了3個協程,前2個協程非常簡單,就是每隔1s向管道輸出資料,第三個協程是不停的從管道取資料,
和之前的例子不一樣的地方是,pump1 和 pump2是2個不同的管道,通過select可以實現在不同管道之間切換,哪個管道有資料就從哪個管道裡面取資料,如果都沒資料就等著,
還有一個定時器功能可以每隔一段時間向管道輸出內容!

最後,值得一說的是,go 內建的web server效能非常強悍,主要就是因為使用了協程,對於每一個web請求,伺服器都會新開一個go協程去處理,
一個伺服器可以輕鬆同時開啟上萬個協程,好了,就說這麼多,感興趣的可以深入瞭解一下GoLang!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.