成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

手把手教你Golang的協程池設計

開發 前端
在很多公司都在陸續的搭建golang的語言棧,大家有沒有想過為什么會出現這種情況?

[[404432]]

本文轉載自微信公眾號「程序員小飯」,作者飯米粒。轉載本文請聯系程序員小飯公眾號。

前言

現在很多公司都在陸續的搭建golang的語言棧,大家有沒有想過為什么會出現這種情況?一是因為go比較適合做中間件,還有一個原因就是go的并發支持比較好,也就是咱們平時所謂的高并發,并發支持離不開協程,當然協程也不是亂用的,需要管理起來,管理協程的方式就是協程池,所以協程池也并沒有那么神秘,今天咱們就來一步一步的揭開協程池的面紗,如果你沒有接觸過go的協程這塊的話也沒有關系,我會盡量寫的詳細。

goroutine(協程)

先來看一個簡單的例子

  1. func go_worker(name string) { 
  2.    for i := 0; i < 5; i++ { 
  3.     fmt.Println("我的名字是"name
  4.     time.Sleep(1 * time.Second
  5.    } 
  6.    fmt.Println(name"執行完畢"
  7. func main() { 
  8.     go_worker("123"
  9.     go_worker("456"
  10.    for i := 0; i < 5; i++ { 
  11.     fmt.Println("我是main"
  12.     time.Sleep(1 * time.Second
  13.    } 

咱們在執行這段代碼的時候,當然是按照順序執行

go_worker("123")->go_worker("456")->我是main執行

輸出結果如下

  1. 我的名字是 123 
  2. 我的名字是 123 
  3. 我的名字是 123 
  4. 我的名字是 123 
  5. 我的名字是 123 
  6. 123 執行完畢 
  7. 我的名字是 456 
  8. 我的名字是 456 
  9. 我的名字是 456 
  10. 我的名字是 456 
  11. 我的名字是 456 
  12. 456 執行完畢 
  13. 我是main 
  14. 我是main 
  15. 我是main 
  16. 我是main 
  17. 我是main 

這樣的執行是并行的,也就是說必須得等一個任務執行結束,下一個任務才會開始,如果某個任務比較慢的話,整個程序的效率是可想而知的,但是在go語言中,支持協程,所以我們可以把上面的代碼改造一下

  1. func go_worker(name string) { 
  2.    for i := 0; i < 5; i++ { 
  3.     fmt.Println("我的名字是"name
  4.     time.Sleep(1 * time.Second
  5.    } 
  6.    fmt.Println(name"執行完畢"
  7. func main() { 
  8.    go go_worker("123")  //協程 
  9.    go go_worker("456")  //協程 
  10.    for i := 0; i < 5; i++ { 
  11.     fmt.Println("我是main"
  12.     time.Sleep(1 * time.Second
  13.    } 

我們在不同的go_worker前面加上了一個go,這樣所有任務就異步的串行了起來,輸出結果如下

  1. 我是main 
  2. 我的名字是 456 
  3. 我的名字是 123 
  4. 我的名字是 123 
  5. 我是main 
  6. 我的名字是 456 
  7. 我是main 
  8. 我的名字是 456 
  9. 我的名字是 123 
  10. 我是main 
  11. 我的名字是 456 
  12. 我的名字是 123 
  13. 我的名字是 456 
  14. 我的名字是 123 
  15. 我是main 

大家可以看到這樣的話就是各自任務執行各自的事情,互相不影響,效率也得到了很大的提升,這就是goroutine

channel(管道)

有了協程之后就會帶來一個新的問題,協程之間是如何通信的?于是就引出了管道這個概念,管道其實很簡單,無非就是往里放數據,往外取數據而已

  1. func worker(c chan int) { 
  2.    num := <-c  //讀取管道中的數據,并輸出 
  3.    fmt.Println("接收到參數c:", num) 
  4. func main() { 
  5.    //channel的創建,需要執行管道數據的類型,我們這里是int 
  6.    c := make(chan int
  7.    //開辟一個協程 去執行worker函數 
  8.    go worker(c) 
  9.    c <- 2  //往管道中寫入2 
  10.    fmt.Println("main"

我們可以看到上述例子,在main函數中,我們定義了一個管道,為int類型,而且往里面寫入了一個2,然后在worker中讀取管道c,就能獲取到2

協程會引發的問題

既然golang中開啟協程這么方便,那么會不會存在什么坑呢?

我們可以看上圖,實際業務中,不同的業務都開啟不同的goroutine來執行,但是在cpu微觀層面上來講,是串行的一個指令一個指令去執行的,只是執行的非常快而已,如果指令來的太多,cpu的切換也會變多,在切換的過程中就需要消耗性能,所以協程池的主要作用就是管理goroutine,限定goroutine的個數

協程池的實現

  • 首先不同的任務,請求過來,直接往entryChannel中寫入,entryChannel再和jobsChannel建立通信
  • 然后我們固定開啟三個協程(不一定是三個,只是用三個舉例子),固定的從jobsChannel中讀取數據,來進行任務處理。
  • 其實本質上,channel就是一道橋梁,做一個中轉的作用,之所以要設計一個jobsChannel和entryChannel,是為了解耦,entryChannel可以完全用做入口,jobsChannel可以做更深入的比如任務優先級,或者加鎖,解鎖等處理

代碼實現

原理清楚了,接下來我們來具體看代碼實現

首先我們來處理任務 task,task無非就是業務中的各種任務,需要能實力化,并且執行,代碼如下

  1. //定義任務Task類型,每一個任務Task都可以抽象成一個函數 
  2. type Task struct{ 
  3.    f func() error //一個task中必須包含一個具體的業務 
  4.  
  5.  
  6. //通過NewTask來創建一個Task 
  7. func NewTask(arg_f func() error) *Task{ 
  8.    t := Task{ 
  9.     f:arg_f, 
  10.    } 
  11.    return &t 
  12.  
  13.  
  14. //Task也需要一個執行業務的方法 
  15. func (t *Task) Execute(){ 
  16.    t.f()//調用任務中已經綁定好的業務方法 

接下來我們來定義協程池

  1. //定義池類型 
  2. type Pool struct{ 
  3.    EntryChannel chan *Task 
  4.    WorkerNum int 
  5.    JobsChanel chan *Task 
  6. //創建一個協程池 
  7. func NewPool(cap int) *Pool{ 
  8.    p := Pool{ 
  9.     EntryChannel: make(chan *Task), 
  10.     JobsChanel: make(chan *Task), 
  11.     WorkerNum: cap, 
  12.    } 
  13.    return &p 

協程池需要創建worker,然后不斷的從JobsChannel內部任務隊列中拿任務開始工作

  1. //協程池創建worker并開始工作 
  2. func (p *Pool) worker(workerId int){ 
  3.     //worker不斷的從JobsChannel內部任務隊列中拿任務 
  4.     for task := range p.JobsChanel{ 
  5.      task.Execute() 
  6.      fmt.Println("workerId",workerId,"執行任務成功"
  7.     } 
  8. EntryChannel獲取Task任務 
  9. func (p *Pool) ReceiveTask(t *Task){ 
  10.    p.EntryChannel <- t 
  11. //讓協程池開始工作 
  12. func (p *Pool) Run(){ 
  13.    //1:首先根據協程池的worker數量限定,開啟固定數量的worker 
  14.    for i:=0; i<p.WorkerNum; i++{ 
  15.     go p.worker(i) 
  16.    } 
  17.    //2:從EntryChannel協程出入口取外部傳遞過來的任務 
  18.    //并將任務送進JobsChannel中 
  19.    for task := range p.EntryChannel{ 
  20.     p.JobsChanel <- task 
  21.    } 
  22.    //3:執行完畢需要關閉JobsChannel和EntryChannel 
  23.    close(p.JobsChanel) 
  24.    close(p.EntryChannel) 

然后我們看在main函數中

  1. //創建一個task 
  2.    t:= NewTask(func() error{ 
  3.     fmt.Println(time.Now()) 
  4.     return nil 
  5.    }) 
  6.  
  7.    //創建一個協程池,最大開啟5個協程worker 
  8.    p:= NewPool(3) 
  9.    //開啟一個協程,不斷的向Pool輸送打印一條時間的task任務 
  10.    go func(){ 
  11.     for { 
  12.      p.ReceiveTask(t)//把任務推向EntryChannel 
  13.     } 
  14.    }() 
  15.    //啟動協程池p 
  16.    p.Run() 

基于上述方法,咱們一個簡單的協程池設計就完成了,當然在實際生產環境中這樣做還是不夠的,不過這些方法能手寫出來,那對golang是相當熟悉了,

 

責任編輯:武曉燕 來源: 程序員小飯
相關推薦

2022-10-19 14:30:59

2011-01-10 14:41:26

2025-05-07 00:31:30

2011-05-03 15:59:00

黑盒打印機

2021-07-14 09:00:00

JavaFX開發應用

2011-02-22 13:46:27

微軟SQL.NET

2021-02-26 11:54:38

MyBatis 插件接口

2021-12-28 08:38:26

Linux 中斷喚醒系統Linux 系統

2021-09-26 16:08:23

CC++clang_forma

2023-04-26 12:46:43

DockerSpringKubernetes

2022-01-08 20:04:20

攔截系統調用

2022-07-27 08:16:22

搜索引擎Lucene

2022-12-07 08:42:35

2022-03-14 14:47:21

HarmonyOS操作系統鴻蒙

2018-03-23 20:45:23

機器學習NLP文本數據

2009-06-15 16:58:57

Java安裝Linux

2022-01-04 08:52:14

博客網站Linux 系統開源

2023-03-29 10:02:36

2021-12-17 18:21:54

大數據流水線設計

2021-12-10 18:19:55

指標體系設計
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产成人精品免费视频大全最热 | 午夜男人的天堂 | 欧美黑人狂野猛交老妇 | 在线一级片 | 天天草天天爱 | 日韩欧美中文字幕在线观看 | 久久国产精品精品国产色婷婷 | 国产麻豆乱码精品一区二区三区 | 天天射网站| 成人一级毛片 | 久久午夜国产精品www忘忧草 | 久久久久久久久99精品 | 午夜免费看视频 | 成人午夜免费在线视频 | 成人免费视频7777777 | a视频在线 | 狠狠色综合网站久久久久久久 | 成人天堂噜噜噜 | 久久久国产精品一区 | 999久久久久久久 | 天天操天天干天天曰 | 国产高清亚洲 | 九九九国产 | 午夜精品久久 | 四虎永久免费黄色影片 | 亚洲精品视频在线看 | 国产乱精品一区二区三区 | 免费能直接在线观看黄的视频 | 亚洲精品久久久蜜桃 | 一区二区三区小视频 | 欧美一区 | 91久久精品国产免费一区 | 亚洲aⅴ| 男插女下体视频 | 亚洲国产欧美一区二区三区久久 | 欧美一级电影免费 | 久久精品欧美一区二区三区不卡 | 日韩 国产 在线 | 日韩精品免费看 | 中文字幕电影在线观看 | 国产一区二区精品自拍 |