日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Go协程与协程池

發布時間:2025/3/15 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go协程与协程池 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. Golang協程

  • golang和其它語言最大區別莫過于goroutine,也就是go的協程,example如下:
package mainimport "fmt" import "time"func go_worker(name string) {for i:=0; i<10; i++ {fmt.Println("this is go worker :" , name)} }func main(){go go_worker("lineshen")go go_worker("glorialu")time.Sleep(time.Second*5) // print effectivefmt.Println("main finished") }

輸出:

this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu main finished

如果不用協程,上面代碼應該三個線程運行,分別是主函數main和 兩個函數。 使用協程之后,實際運行兩個線程,這就是并發的好處。

Note當使用go啟動協程之后,這2個函數就被切換到協程里面執行了,但是這時候主線程結束了,這2個協程還沒來得及執行就會掛了!所以不在main中加time進行sleep就無法看到協程中的執行結果。

  • 多次運行,其結果可能如下:
this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : glorialu this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen this is go worker : lineshen main finished

會發現lineshen和 glorialu 的順序不是固定的,這進一步說明了一個問題,那就是多個協程是同時執行的。不過睡眠這種做法肯定是不靠譜的,go 自帶一個WaitGroup可以解決這個問題,。

  • WaitGroup示例如下:
package mainimport ("fmt""time""sync") var wg sync.WaitGroupfunc go_worker(name string) {for i:=0; i<10; i++ {fmt.Println("this is go worker :" , name)}wg.Done() }func main(){wg.Add(2)go go_worker("lineshen")go go_worker("glorialu")wg.Wait()// time.Sleep(time.Second*5) // print effectivefmt.Println("main finished") }

sync.WaitGroup也起到了time.Sleep的效果。但是協程內部的執行順序是不確定的

sync.WaitGroup用法: var 是聲明了一個全局變量 wg,類型是sync.WaitGroup,wg.Add(2) 指2個goroutine需要執行,
wg.Done 相當于 wg.Add(-1) 意思就是該協程執行結束;wg.Wait()通信主線程wait,等2個協程都執行完再退出

應用實例:從3個庫取不同的數據匯總處理,最簡單寫法就是查3次庫,但是這3次查詢必須按順序執行,大部分編程語言的代碼執行順序都是從上到下,假如一個查詢耗時1s,3個查詢就是3s,但是使用協程你可以讓這3個查詢同時進行,也就是1s就可以搞定。

2. Golang協程池與channel機制

  • 同類型channel

如何將協程中結果返回給主線程?golang提供了channel機制。示例如下:

package mainimport ("fmt""sync") var wg sync.WaitGroupfunc go_worker(name string, ch chan string) {for i:=0; i<10; i++ {ch <- name}wg.Done() }func main(){wg.Add(2)ch := make(chan string)go go_worker("lineshen", ch)go go_worker("glorialu", ch)for {fmt.Println("this is go worker :" , <-ch)}wg.Wait()fmt.Println("main finished") }

代碼執行結果如下。就是實例化了一個channel,go啟動的協程同時向這個2個管道輸出數據,主線程使用了一個for循環從管道里面取數據。其實就是一個生產者-消費者模式,與redis隊列有點像。下面也可以看到lineshen和glorialu進入管道的順序是不固定的。如果實驗發現輸出順序固定的,那是因為電腦跑的太快了,可以把循環開的大一點。

this is go worker : glorialu this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : lineshen fatal error: all goroutines are asleep - deadlock!

也看到發生了致命的錯誤:fatal error: all goroutines are asleep - deadlock! 所有的協程都睡眠了,程序監測到死鎖!go的channel默認是阻塞的(假如不設置緩存),需要“放一個-取一個”。如果channel里面放置的數據沒有取出來,程序就會一直等下去,死鎖了;同時,如果channel里面沒有放置數據,主線程去取數據也會發生死鎖!

如何解決這個問題呢?標準的做法是主動關閉管道,或者知道應該什么時候關閉管道。對于示例代碼,可以簡單加入一個計數器判斷何時channel中的數據可以完全被取出。如下:

package mainimport ("fmt""sync") var wg sync.WaitGroupfunc go_worker(name string, ch chan string) {for i:=0; i<10; i++ {ch <- name}wg.Done() }func main(){wg.Add(2)ch := make(chan string)go go_worker("lineshen", ch)go go_worker("glorialu", ch)counter := 1for {fmt.Println("this is go worker :" , <-ch)if counter >= 20 {close(ch)break}counter++}wg.Wait()fmt.Println("main finished") }

輸出如下:

this is go worker : glorialu this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : glorialu this is go worker : lineshen this is go worker : lineshen main finished
  • 多種類型channel,使用select進行判斷
package mainimport ("time""fmt")func go_worker1_int(ch chan int) {for i:=0; ; i++ {ch <- itime.Sleep(time.Duration(time.Second))} }func go_worker2_string(name string, ch chan string){for i:=0; ; i++ {ch <- nametime.Sleep(time.Duration(time.Second))} } func go_worker3_print(ch1 chan int, ch2 chan string){chRate := time.Tick(time.Duration(time.Second))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...\n")}} }func main(){ch1 := make(chan int)ch2 := make(chan string)go go_worker1_int(ch1)go go_worker2_string("lineshen", ch2)go_worker3_print(ch1, ch2)time.Sleep(time.Duration(time.Second*10))fmt.Println("main finished") }

輸出結果如下:

Received on channel 1: 0 Received on channel 2: lineshen Received on channel 1: 1 Received on channel 2: lineshen log... Received on channel 2: lineshen Received on channel 1: 2 log... log... Received on channel 1: 3 Received on channel 2: lineshen log... Received on channel 1: 4 Received on channel 2: lineshen log...

該程序建立了2個管道一個傳輸int,一個傳輸string,同時啟動了3個協程,前2個協程非常簡單,就是每隔1s向管道輸出數據,第三個協程是不停的從管道取數據,并通過定時器功能可以每隔一段時間向管道輸出內容!

不同的是,go_worker1_int和go_worker2_string是2個不同的管道,通過select可以實現在不同管道之間切換,哪個管道有數據就從哪個管道里面取數據,如果都沒數據就等著。

總結

以上是生活随笔為你收集整理的Go协程与协程池的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。