Go語言協(xié)程池的實(shí)現(xiàn)示例
Go語言雖然有著高效的GMP調(diào)度模型,理論上支持成千上萬的goroutine,但是goroutine過多,對(duì)調(diào)度,gc以及系統(tǒng)內(nèi)存都會(huì)造成壓力,這樣會(huì)使我們的服務(wù)性能不升反降。常用做法可以用池化技術(shù),構(gòu)造一個(gè)協(xié)程池,把進(jìn)程中的協(xié)程控制在一定的數(shù)量,防止系統(tǒng)中goroutine過多,影響服務(wù)性能。
協(xié)程池模型
協(xié)程池簡(jiǎn)單理解就是有一個(gè)池子一樣的東西,里面裝有固定數(shù)量的goroutine,當(dāng)有一個(gè)任務(wù)到來的時(shí)候,會(huì)將這個(gè)任務(wù)交給池子里的一個(gè)空閑的goroutine去處理,如果池子里沒有空閑的goroutine了,任務(wù)就會(huì)阻塞等待。所以協(xié)程池有三個(gè)角色Worker,Task,Pool。
屬性定義
- Worker:用于執(zhí)行任務(wù)的goroutine
- Task: 具體的任務(wù)
- Pool: 池子
下面看一下各個(gè)角色的定義:
Task定義
Task有一個(gè)函數(shù)成員,表示這個(gè)task具體的執(zhí)行邏輯:
type Task struct {
f func() error // 具體的執(zhí)行邏輯
}Pool定義
Pool有兩個(gè)成員,Capacity表示池子里的worker的數(shù)量,即工作的goroutine的數(shù)量,JobCh表示任務(wù)隊(duì)列用于存放任務(wù),goroutine從這個(gè)JobCh獲取任務(wù)執(zhí)行任務(wù)邏輯:
type Pool struct {
RunningWorkers int64 // 運(yùn)行著的worker數(shù)量
Capacity int64 // 協(xié)程池worker容量---goroutine數(shù)量
JobCh chan *Task // 用于worker取任務(wù)
sync.Mutex
}Worker 定義
// p為Pool對(duì)象指針
for task := range p.JobCh {
do ...
}執(zhí)行任務(wù)單元,簡(jiǎn)單理解就是干活的goroutine,這個(gè)worker其實(shí)只做一件事情,就是不斷的從任務(wù)隊(duì)列里面取任務(wù)執(zhí)行,而worker的數(shù)量就是協(xié)程池里協(xié)程的數(shù)量,由Pool的參數(shù)指定。
方法定義
NewTask用于創(chuàng)建一個(gè)任務(wù),參數(shù)是一個(gè)函數(shù),返回值是一個(gè)Task類型。
func NewTask(funcArg func() error) *Task
NewPool返回一個(gè)協(xié)程數(shù)量固定為Capacity協(xié)程池對(duì)象指針,其任務(wù)隊(duì)列的長(zhǎng)度為taskNum。
func NewPool(Capacity int, taskNum int) *Pool
接下來主要介紹協(xié)程池的各個(gè)方法:
AddTask方法是往協(xié)程池添加任務(wù),如果當(dāng)前運(yùn)行著的worker數(shù)量小于協(xié)程池worker容量,則立即啟動(dòng)一個(gè)協(xié)程worker來處理任務(wù),否則將任務(wù)添加到任務(wù)隊(duì)列。
func (p *Pool) AddTask(task *Task)
Run方法將協(xié)程池跑起來,啟動(dòng)一個(gè)worker來處理任務(wù)。
func (p *Pool) Run()
協(xié)程池處理任務(wù)流程圖:

協(xié)程池實(shí)現(xiàn):
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
type Task struct {
f func() error // 具體的任務(wù)邏輯
}
func NewTask(funcArg func() error) *Task {
return &Task{
f: funcArg,
}
}
type Pool struct {
RunningWorkers int64 // 運(yùn)行著的worker數(shù)量
Capacity int64 // 協(xié)程池worker容量
JobCh chan *Task // 用于worker取任務(wù)
sync.Mutex
}
func NewPool(capacity int64, taskNum int) *Pool {
return &Pool{
Capacity: capacity,
JobCh: make(chan *Task, taskNum),
}
}
func (p *Pool) GetCap() int64 {
return p.Capacity
}
func (p *Pool) incRunning() { // runningWorkers + 1
atomic.AddInt64(&p.RunningWorkers, 1)
}
func (p *Pool) decRunning() { // runningWorkers - 1
atomic.AddInt64(&p.RunningWorkers, -1)
}
func (p *Pool) GetRunningWorkers() int64 {
return atomic.LoadInt64(&p.RunningWorkers)
}
func (p *Pool) run() {
p.incRunning()
go func() {
defer func() {
p.decRunning()
}()
for task := range p.JobCh {
task.f()
}
}()
}
// AddTask 往協(xié)程池添加任務(wù)
func (p *Pool) AddTask(task *Task) {
// 加鎖防止啟動(dòng)多個(gè) worker
p.Lock()
defer p.Unlock()
if p.GetRunningWorkers() < p.GetCap() { // 如果任務(wù)池滿, 則不再創(chuàng)建 worker
// 創(chuàng)建啟動(dòng)一個(gè) worker
p.run()
}
// 將任務(wù)推入隊(duì)列, 等待消費(fèi)
p.JobCh <- task
}
func main() {
// 創(chuàng)建任務(wù)池
pool := NewPool(3, 10)
for i := 0; i < 20; i++ {
// 任務(wù)放入池中
pool.AddTask(NewTask(func() error {
fmt.Printf("I am Task\n")
return nil
}))
}
time.Sleep(1e9) // 等待執(zhí)行
}運(yùn)行結(jié)果:
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
I am Task
程序創(chuàng)建了一個(gè)Worker數(shù)量為3,任務(wù)隊(duì)列長(zhǎng)度為10的協(xié)程池,往里面添加了20個(gè)任務(wù),可以看到輸出,一直只有3個(gè)worker在做任務(wù),起到了控制goroutine數(shù)量的作用。
到此這篇關(guān)于Go語言協(xié)程池的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Go語言協(xié)程池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang使用Gin框架實(shí)現(xiàn)HTTP響應(yīng)格式統(tǒng)一處理
在gin框架中,我們可以定義一個(gè)中間件來處理統(tǒng)一的HTTP響應(yīng)格式,本文主要為大家介紹了具體是怎么定義實(shí)現(xiàn)這樣的中間件的,感興趣的小伙伴可以了解一下2023-07-07
GoLang并發(fā)機(jī)制探究goroutine原理詳細(xì)講解
goroutine是Go語言提供的語言級(jí)別的輕量級(jí)線程,在我們需要使用并發(fā)時(shí),我們只需要通過 go 關(guān)鍵字來開啟 goroutine 即可。這篇文章主要介紹了GoLang并發(fā)機(jī)制goroutine原理,感興趣的可以了解一下2022-12-12
GoRoutines高性能同時(shí)進(jìn)行多個(gè)Api調(diào)用實(shí)現(xiàn)
這篇文章主要為大家介紹了GoRoutines高性能同時(shí)進(jìn)行多個(gè)Api調(diào)用實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
go?smtp實(shí)現(xiàn)郵件發(fā)送示例詳解
這篇文章主要為大家介紹了go?smtp實(shí)現(xiàn)郵件發(fā)送示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
Go語言實(shí)現(xiàn)二進(jìn)制與十進(jìn)制互轉(zhuǎn)的示例代碼
這篇文章主要和大家詳細(xì)介紹了Go語言中實(shí)現(xiàn)二進(jìn)制與十進(jìn)制互相轉(zhuǎn)換的示例代碼,文中的代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-05-05

