grpool goroutine池詳解 | 協程管理
highlight: a11y-dark theme: smartblue
持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第9天,點選檢視活動詳情
前言
goroutine協程非常輕量級,這也是為什麼go支援高併發,但是goroutine頻繁建立銷燬對GC的壓力比較大。
grpool的作用就是複用goroutine,減少頻繁建立銷燬的效能消耗。
名詞概念
Pool: goroutine池,用於管理若干可複用的goroutine協程資源
Worker: 池物件中參與任務執行的goroutine,一個worker可以執行若干個job,直到佇列中再無等待的job
Job:新增到池物件的任務佇列中等待執行的任務,是一個func()方法,一個job同時只能被一個worker獲取並執行。
使用示例
使用預設的協程池,限制100個協程執行1000個任務
pool.Size() 獲得當前工作的協程數量
pool.Jobs() 獲得當前池中待處理的任務數量
```go package main
import ( "fmt" "github.com/gogf/gf/os/grpool" "github.com/gogf/gf/os/gtimer" "sync" "time" )
func main() { pool := grpool.New(100)
//新增1千個任務 for i := 0; i < 1000; i++ { _ = pool.Add(job) }
fmt.Println("worker:", pool.Size()) //當前工作的協程數量 fmt.Println("jobs:", pool.Jobs()) //當前池中待處理的任務數量
gtimer.SetInterval(time.Second, func() { fmt.Println("worker:", pool.Size()) //當前工作的協程數 fmt.Println("jobs:", pool.Jobs()) //當前池中待處理的任務數 })
//阻止程序結束 select {} }
//任務方法 func job() { time.Sleep(time.Second) } ```
列印結果
是不是灰常簡單~
踩坑之旅
一個簡單的場景,請使用協程列印0~9。
常犯的錯誤
大家看下面的程式碼有沒有問題,請預測一下列印結果。
go
wg := sync.WaitGroup{}
for i := 0; i < 9; i++ {
wg.Add(1)
go func() {
fmt.Println(i)
wg.Done()
}()
}
wg.Wait()
不用著急看答案
.
.
.
猜一下列印結果是什麼
列印結果
分析原因
對於非同步執行緒/協程來講,函式進行非同步執行註冊時,該函式並未真正開始執行(註冊時只在goroutine
的棧中儲存了變數i
的記憶體地址),而一旦開始執行時函式才會去讀取變數i
的值,而這個時候變數i
的值已經自增到了9
。
正確寫法
go
wg := sync.WaitGroup{}
for i := 0; i < 9; i++ {
wg.Add(1)
go func(v int) {
fmt.Println(v)
wg.Done()
}(i)
}
wg.Wait()
列印結果
使用grpool
使用grpool和使用go一樣,都需要把當前變數i的值賦值給一個不會改變的臨時變數,在函式中使用該臨時變數而不是直接使用變數i
。
錯誤程式碼
go
wg := sync.WaitGroup{}
for i := 0; i < 9; i++ {
wg.Add(1)
_ = grpool.Add(func() {
fmt.Println(i) //列印結果都是9
wg.Done()
})
}
wg.Wait()
列印結果
正確程式碼
go
wg := sync.WaitGroup{}
for i := 0; i < 9; i++ {
wg.Add(1)
v := i //grpoll.add() 的引數只能是不帶引數的匿名函式 因此只能以設定臨時變數的方式賦值
_ = grpool.Add(func() {
fmt.Println(v)
wg.Done()
})
}
wg.Wait()
列印結果
總結
通過這篇文章我們瞭解到:grpool的作用就是複用goroutine,減少頻繁建立銷燬的效能消耗。
也瞭解到使用協程容易犯的錯誤,以及用臨時變數的方式來解決問題。
說句題外話:grpool的基礎概念:Pool、Worke、Job 和我之前設計的派單系統簡直一模一樣。
最後
感謝閱讀,歡迎大家三連:點贊、收藏、~~投幣~~(關注)!!!
- 爆肝兩千字整理《Go學習路線圖》| 文末投稿送投影
- grpool goroutine池詳解 | 協程管理
- GoFrame如何實現順序性校驗
- GoFrame 如何優雅的共享變數 | Context的使用
- GoFrame 錯誤處理的常用方法&錯誤碼的使用
- 協同開發時巧用gitignore | 巧用中介軟體 避免網路請求時攜帶登入資訊
- 【今日總結】Go本地測試 如何解耦 任務拆解&溝通
- GoFrame資料校驗之校驗結果 | Error介面物件
- GoFrame基於效能測試得知什麼場景下使用grpool比較好
- GoFrame資料校驗之校驗物件
- GoFrame 如何優雅的快取查詢結果
- GoFrame gredis 如何優雅的取值和型別轉換
- GoFrame gredis 硬核解析 | DoVar、Conn連線物件、自動序列化
- GoFrame gredis 配置管理 | 配置檔案、配置方法的對比
- GoFrame 記憶體快取之gcache
- Git 重新命名遠端分支 | 操作不規範,親人兩行淚。
- 最近的踩坑分享 | 技術文件和需求拆解
- GoFrame 通用型別變數gvar | 對比 interface{}
- GoFrame gtree 使用入門 | 養成讀原始碼的好習慣
- GoFrame gset使用技巧總結 | 出棧、子集判斷、序列化、遍歷修改