当前位置: 首页 > news >正文

Go并发:使用sync.Pool来性能优化

简介

在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.创建一个sync.Pool对象
    pool := &sync.Pool{
       New: func() interface{} {
          fmt.Println("Creating a new object")
          return &Object{}
       },
    }
    // 2.pool.Get()方法从池中获取一个对象。如果池中有可用的对象,Get()方法将返回其中一个;否则,它将返回一个新创建的对象
    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对该方案进行优化。
观察在棋牌游戏的代码,虽然解决了每次都要New一个对象的问题,但还存在几个优化点:

不能只能缓存特定的棋牌室类型对象;
并发安全问题

原来是通过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. 并发安全问题

2.1 修改模型

为了方便观察,给每个房间(棋牌室)增加一个创建时间

type ChessBoard struct {
    Cards map[int]*ChessPiece
    Time  time.Time
} 

2.2 并发测试

启动多个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的并发情况下,是安全,另外可以观察到,sync.Pool没有一直【Creating a new object】去New很多棋牌室。

小结

sync.Pool是Go语言标准库中的一个类型,它提供了对象的缓存池功能。它的主要用途是存储那些可以被复用的临时对象,以便在需要时快速获取,而不是每次都进行新的对象分配。且多个 goroutine 同时使用 Pool 是安全的。
本文简述了sync.Pool的基础使用,以及了如何使用其对实践棋牌室游戏的案例进行优化过程。

参考

官方doc
设计模式:flyweight(享元

相关文章:

  • leetcode_39 组合总和
  • 重入漏洞EtherStore
  • VSCode 开发 Vue 语法提示
  • python版opencv人脸训练与人脸识别
  • 【Matlab2016】Matlab中文版的下载、安装、激活(不建议安装过高版本!!)
  • 如何创建加载项(1)
  • Git总结
  • 2016年亚太杯APMCM数学建模大赛C题影视评价与定制求解全过程文档及程序
  • WinDbg 远程调试遇到IP为:169.254.xx.xx 的处理
  • 【Linux】操作系统以及虚拟机的安装与配置
  • 容联七陌百度营销通BCP解决方案,让营销更精准
  • 【Linux】【驱动】设备树中设备节点的挂载
  • 电脑msvcp100.dll丢失了怎么办?详细的5个修复方法
  • Maven配置阿里云中央仓库settings.xml
  • 【软件教程】如何用C++检查TCP或UDP端口是否被占用
  • Arrays 中的 asList()方法
  • MS COCO数据集的评价标准以及不同指标的选择推荐(AP、mAP、MS COCO、AR、@、0.5、0.75、1、目标检测、评价指标)
  • Android帧率监测与优化技巧
  • GRS不止局限于纺织行业
  • 安防监控项目---环境配置
  • 媒体起底“速成洋文凭”灰产链,专家:我们要给学历“祛魅”
  • 中国德国商会报告:76%在华德企受美国关税影响,但对华投资战略依然稳固
  • 从“重规模”向“重回报”转变,公募基金迎系统性改革
  • 湖北奥莱斯轮胎公司逃避监管排放大气污染物被罚25万元
  • 8小时《大师与玛格丽特》:长度可以是特点,但不是价值标准
  • 云南禄丰尾矿坍塌事故搜救正在进行,被掩埋的四辆工程车已找到