日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
手把手教你Golang的協(xié)程池設(shè)計(jì)

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

成都創(chuàng)新互聯(lián)公司專注于成都網(wǎng)站建設(shè)、做網(wǎng)站、網(wǎng)頁設(shè)計(jì)、網(wǎng)站制作、網(wǎng)站開發(fā)。公司秉持“客戶至上,用心服務(wù)”的宗旨,從客戶的利益和觀點(diǎn)出發(fā),讓客戶在網(wǎng)絡(luò)營銷中找到自己的駐足之地。尊重和關(guān)懷每一位客戶,用嚴(yán)謹(jǐn)?shù)膽B(tài)度對待客戶,用專業(yè)的服務(wù)創(chuàng)造價(jià)值,成為客戶值得信賴的朋友,為客戶解除后顧之憂。

前言

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

goroutine(協(xié)程)

先來看一個(gè)簡單的例子

  
 
 
 
  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í)行這段代碼的時(shí)候,當(dāng)然是按照順序執(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í)行是并行的,也就是說必須得等一個(gè)任務(wù)執(zhí)行結(jié)束,下一個(gè)任務(wù)才會(huì)開始,如果某個(gè)任務(wù)比較慢的話,整個(gè)程序的效率是可想而知的,但是在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前面加上了一個(gè)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é)程之后就會(huì)帶來一個(gè)新的問題,協(xié)程之間是如何通信的?于是就引出了管道這個(gè)概念,管道其實(shí)很簡單,無非就是往里放數(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.    //開辟一個(gè)協(xié)程 去執(zhí)行worker函數(shù) 
  8.    go worker(c) 
  9.    c <- 2  //往管道中寫入2 
  10.    fmt.Println("main") 

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

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

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

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

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

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

代碼實(shí)現(xiàn)

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

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

  
 
 
 
  1. //定義任務(wù)Task類型,每一個(gè)任務(wù)Task都可以抽象成一個(gè)函數(shù) 
  2. type Task struct{ 
  3.    f func() error //一個(gè)task中必須包含一個(gè)具體的業(yè)務(wù) 
  4.  
  5.  
  6. //通過NewTask來創(chuàng)建一個(gè)Task 
  7. func NewTask(arg_f func() error) *Task{ 
  8.    t := Task{ 
  9.     f:arg_f, 
  10.    } 
  11.    return &t 
  12.  
  13.  
  14. //Task也需要一個(gè)執(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)建一個(gè)協(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é)程池需要?jiǎng)?chuàng)建worker,然后不斷的從JobsChannel內(nèi)部任務(wù)隊(duì)列中拿任務(wù)開始工作

  
 
 
 
  1. //協(xié)程池創(chuàng)建worker并開始工作 
  2. func (p *Pool) worker(workerId int){ 
  3.     //worker不斷的從JobsChannel內(nèi)部任務(wù)隊(duì)列中拿任務(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
  15.     go p.worker(i) 
  16.    } 
  17.    //2:從EntryChannel協(xié)程出入口取外部傳遞過來的任務(wù) 
  18.    //并將任務(wù)送進(jìn)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)建一個(gè)task 
  2.    t:= NewTask(func() error{ 
  3.     fmt.Println(time.Now()) 
  4.     return nil 
  5.    }) 
  6.  
  7.    //創(chuàng)建一個(gè)協(xié)程池,最大開啟5個(gè)協(xié)程worker 
  8.    p:= NewPool(3) 
  9.    //開啟一個(gè)協(xié)程,不斷的向Pool輸送打印一條時(shí)間的task任務(wù) 
  10.    go func(){ 
  11.     for { 
  12.      p.ReceiveTask(t)//把任務(wù)推向EntryChannel 
  13.     } 
  14.    }() 
  15.    //啟動(dòng)協(xié)程池p 
  16.    p.Run() 

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

責(zé)任編輯:武曉燕
來源: 程序員小飯 協(xié)程池 Golang 設(shè)計(jì)


文章題目:手把手教你Golang的協(xié)程池設(shè)計(jì)
網(wǎng)站鏈接:http://m.5511xx.com/article/coshgjg.html