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