golang并發(fā)之使用sync.Pool優(yōu)化性能
簡介
在Go提供如何實(shí)現(xiàn)對象的緩存池功能?常用一種實(shí)現(xiàn)方式是:sync.Pool, 其旨在緩存已分配但未使用的項(xiàng)目以供以后重用,從而減輕垃圾收集器(GC)的壓力。
快速使用
sync.Pool的結(jié)構(gòu)也比較簡單,常用的方法有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)
接著,通過一個(gè)簡單的例子,來看看是如何使用的
package main
import (
"fmt"
"sync"
)
type Object struct {
ID int
// ...
}
func main() {
// 1.創(chuàng)建一個(gè)sync.Pool對象
pool := &sync.Pool{
New: func() interface{} {
fmt.Println("Creating a new object")
return &Object{}
},
}
// 2.pool.Get()方法從池中獲取一個(gè)對象。如果池中有可用的對象,Get()方法將返回其中一個(gè);否則,它將返回一個(gè)新創(chuàng)建的對象
obj := pool.Get().(*Object)
// 3.操作對象
obj.ID = 1
// 4.調(diào)用pool.Put()方法將對象放回池中
pool.Put(obj)
objBar := pool.Get().(*Object)
fmt.Println("Object ID:", objBar.ID)
}
實(shí)踐應(yīng)用
在之前的文章中有提到的享元模式設(shè)計(jì)模式:flyweight(享元)的在棋牌游戲的應(yīng)用的案例。今天我們使用sync.Pool對該方案進(jìn)行優(yōu)化。 觀察在棋牌游戲的代碼,雖然解決了每次都要New一個(gè)對象的問題,但還存在幾個(gè)優(yōu)化點(diǎn):
- 不能只能緩存特定的棋牌室類型對象;
- 并發(fā)安全問題
原來是通過Factory工廠+Map實(shí)現(xiàn)享元模式,截取其中部分代碼如下
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.重構(gòu)Factory
接著,我們同sync.Pool修改一下Factory的實(shí)現(xiàn):
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 修改模型
為了方便觀察,給每個(gè)房間(棋牌室)增加一個(gè)創(chuàng)建時(shí)間
type ChessBoard struct {
Cards map[int]*ChessPiece
Time time.Time
}
2.2 并發(fā)測試
啟動(dòng)多個(gè)goroutine進(jìn)行測試
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
可見,在多個(gè)goroutine的并發(fā)情況下,是安全,另外可以觀察到,sync.Pool沒有一直【Creating a new object】去New很多棋牌室。
小結(jié)
sync.Pool是Go語言標(biāo)準(zhǔn)庫中的一個(gè)類型,它提供了對象的緩存池功能。它的主要用途是存儲(chǔ)那些可以被復(fù)用的臨時(shí)對象,以便在需要時(shí)快速獲取,而不是每次都進(jìn)行新的對象分配。且多個(gè) goroutine 同時(shí)使用 Pool 是安全的。
本文簡述了sync.Pool的基礎(chǔ)使用,以及了如何使用其對實(shí)踐棋牌室游戲的案例進(jìn)行優(yōu)化過程。
以上就是golang并發(fā)之使用sync.Pool優(yōu)化性能的詳細(xì)內(nèi)容,更多關(guān)于go sync.Pool的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go?gRPC教程實(shí)現(xiàn)Simple?RPC
這篇文章主要為大家介紹了Go?gRPC教程實(shí)現(xiàn)Simple?RPC示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
golang框架中跨服務(wù)的最佳通信協(xié)議和工具
在 go 框架中實(shí)現(xiàn)跨服務(wù)通信的最佳實(shí)踐包括使用 grpc(適用于低延遲高吞吐量)、http 客戶端(適用于 restful api)和消息隊(duì)列(適用于異步解耦通信),在選擇通信方式時(shí),應(yīng)考慮服務(wù)交互模式、性能要求和部署環(huán)境等因素2024-06-06
利用golang的字符串解決leetcode翻轉(zhuǎn)字符串里的單詞
這篇文章主要介紹了利用golang的字符串解決leetcode翻轉(zhuǎn)字符串里的單詞,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
Golang中文件目錄操作的實(shí)現(xiàn)步驟詳解
在Golang中,文件目錄是指計(jì)算機(jī)文件系統(tǒng)中的文件夾或目錄。目錄是用于組織和存儲(chǔ)文件的一種方式,可以包含文件和其他子目錄,本文主要介紹了Golang中文件目錄操作的實(shí)現(xiàn)方法,需要的朋友可以參考下2023-05-05

