golang并發(fā)之使用sync.Pool優(yōu)化性能
簡介
在Go提供如何實現對象的緩存池功能?常用一種實現方式是:sync.Pool, 其旨在緩存已分配但未使用的項目以供以后重用,從而減輕垃圾收集器(GC)的壓力。
快速使用
sync.Pool的結構也比較簡單,常用的方法有Get、Put
type Pool struct {
local unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal
localSize uintptr // size of the local array
victim unsafe.Pointer // local from previous cycle
victimSize uintptr // size of victims array
// New optionally specifies a function to generate
// a value when Get would otherwise return nil.
// It may not be changed concurrently with calls to Get.
New func() any
}
func (p *Pool) Get() any
func (p *Pool) Put(x any)
接著,通過一個簡單的例子,來看看是如何使用的
package main
import (
"fmt"
"sync"
)
type Object struct {
ID int
// ...
}
func main() {
// 1.創(chuàng)建一個sync.Pool對象
pool := &sync.Pool{
New: func() interface{} {
fmt.Println("Creating a new object")
return &Object{}
},
}
// 2.pool.Get()方法從池中獲取一個對象。如果池中有可用的對象,Get()方法將返回其中一個;否則,它將返回一個新創(chuàng)建的對象
obj := pool.Get().(*Object)
// 3.操作對象
obj.ID = 1
// 4.調用pool.Put()方法將對象放回池中
pool.Put(obj)
objBar := pool.Get().(*Object)
fmt.Println("Object ID:", objBar.ID)
}
實踐應用
在之前的文章中有提到的享元模式設計模式:flyweight(享元)的在棋牌游戲的應用的案例。今天我們使用sync.Pool對該方案進行優(yōu)化。 觀察在棋牌游戲的代碼,雖然解決了每次都要New一個對象的問題,但還存在幾個優(yōu)化點:
- 不能只能緩存特定的棋牌室類型對象;
- 并發(fā)安全問題
原來是通過Factory工廠+Map實現享元模式,截取其中部分代碼如下
package design_mode
import "fmt"
var chessPieceUnit = map[int]*ChessPiece{
1: {
Name: "車",
Color: "紅",
PositionX: 1,
PositionY: 11,
},
2: {
Name: "馬",
Color: "黑",
PositionX: 2,
PositionY: 2,
},
// 其他棋子
}
func NewChessPieceUnitFactory() *ChessBoard {
board := &ChessBoard{Cards: map[int]*ChessPiece{}}
for id := range chessPieceUnit {
board.Cards[id] = chessPieceUnit[id]
}
return board
}
1.重構Factory
接著,我們同sync.Pool修改一下Factory的實現:
pool := &sync.Pool{
New: func() interface{} {
fmt.Println("Creating a new object")
return NewChessBoard()
},
}
game1 := pool.Get().(*ChessBoard)
game2 := pool.Get().(*ChessBoard)
fmt.Println(game1)
fmt.Println(game2)
fmt.Println(game1.Cards[0] == game2.Cards[0])
2. 并發(fā)安全問題
2.1 修改模型
為了方便觀察,給每個房間(棋牌室)增加一個創(chuàng)建時間
type ChessBoard struct {
Cards map[int]*ChessPiece
Time time.Time
}
2.2 并發(fā)測試
啟動多個goroutine進行測試
func main() {
pool := &sync.Pool{
New: func() interface{} {
fmt.Println("Creating a new object")
return NewChessBoard()
},
}
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
obj := pool.Get().(*ChessBoard)
obj.Time = time.Now()
pool.Put(obj)
fmt.Printf("Object ID: %v\n", obj.Time)
}(i)
}
wg.Wait()
}
輸出如下:
Creating a new object
Creating a new object
Object ID: 2023-10-22 15:41:50.309343 +0800 CST m=+0.003511901
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
Object ID: 2023-10-22 15:41:50.3117423 +0800 CST m=+0.005911201
可見,在多個goroutine的并發(fā)情況下,是安全,另外可以觀察到,sync.Pool沒有一直【Creating a new object】去New很多棋牌室。
小結
sync.Pool是Go語言標準庫中的一個類型,它提供了對象的緩存池功能。它的主要用途是存儲那些可以被復用的臨時對象,以便在需要時快速獲取,而不是每次都進行新的對象分配。且多個 goroutine 同時使用 Pool 是安全的。
本文簡述了sync.Pool的基礎使用,以及了如何使用其對實踐棋牌室游戲的案例進行優(yōu)化過程。
以上就是golang并發(fā)之使用sync.Pool優(yōu)化性能的詳細內容,更多關于go sync.Pool的資料請關注腳本之家其它相關文章!
相關文章
利用golang的字符串解決leetcode翻轉字符串里的單詞
這篇文章主要介紹了利用golang的字符串解決leetcode翻轉字符串里的單詞,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12

