自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

手把手教你Golang的協(xié)程池設(shè)計

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

[[404432]]

本文轉(zhuǎn)載自微信公眾號「程序員小飯」,作者飯米粒。轉(zhuǎn)載本文請聯(lián)系程序員小飯公眾號。

前言

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

goroutine(協(xié)程)

先來看一個簡單的例子

  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"執(zhí)行完畢"
  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.    } 

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

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

輸出結(jié)果如下

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

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

  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"執(zhí)行完畢"
  7. func main() { 
  8.    go go_worker("123")  //協(xié)程 
  9.    go go_worker("456")  //協(xié)程 
  10.    for i := 0; i < 5; i++ { 
  11.     fmt.Println("我是main"
  12.     time.Sleep(1 * time.Second
  13.    } 

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

  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 

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

channel(管道)

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

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

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

協(xié)程會引發(fā)的問題

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

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

協(xié)程池的實現(xiàn)

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

代碼實現(xiàn)

原理清楚了,接下來我們來具體看代碼實現(xiàn)

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

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

接下來我們來定義協(xié)程池

  1. //定義池類型 
  2. type Pool struct{ 
  3.    EntryChannel chan *Task 
  4.    WorkerNum int 
  5.    JobsChanel chan *Task 
  6. //創(chuàng)建一個協(xié)程池 
  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 

協(xié)程池需要創(chuàng)建worker,然后不斷的從JobsChannel內(nèi)部任務(wù)隊列中拿任務(wù)開始工作

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

然后我們看在main函數(shù)中

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

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

 

責(zé)任編輯:武曉燕 來源: 程序員小飯
相關(guān)推薦

2022-10-19 14:30:59

2021-07-14 09:00:00

JavaFX開發(fā)應(yīng)用

2011-01-10 14:41:26

2011-05-03 15:59:00

黑盒打印機

2021-09-26 16:08:23

CC++clang_forma

2022-01-08 20:04:20

攔截系統(tǒng)調(diào)用

2023-04-26 12:46:43

DockerSpringKubernetes

2022-12-07 08:42:35

2022-07-27 08:16:22

搜索引擎Lucene

2022-03-14 14:47:21

HarmonyOS操作系統(tǒng)鴻蒙

2011-02-22 13:46:27

微軟SQL.NET

2021-12-28 08:38:26

Linux 中斷喚醒系統(tǒng)Linux 系統(tǒng)

2021-02-26 11:54:38

MyBatis 插件接口

2023-03-29 10:02:36

2021-12-10 18:19:55

指標體系設(shè)計

2021-12-17 18:21:54

大數(shù)據(jù)流水線設(shè)計

2017-07-07 11:01:04

Spark性能調(diào)優(yōu)

2020-08-12 07:41:39

SQL 優(yōu)化語句

2021-07-01 09:31:50

MySQL SQL 語句數(shù)據(jù)庫

2024-04-02 08:58:13

點贊
收藏

51CTO技術(shù)棧公眾號