开发者

golang并发之使用sync.Pool优化性能

目录
  • 简介
  • 快速使用
  • 实践应用
    • 1.重构Factory
    • 2. 并发安全问题
  • 小结

    简介

    在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 functionjavascript 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修改一下Factjavascriptory的实现:

    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:android41:50.3117423 +0800 CST m=+0.005911201

    Object ID: 2023-js10-22 15:41:50.3117423 +0800 CST m=+0.005911201

    Object ID: 2023-10-22 15:41:50.php3117423 +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的基础使用,以及了如何使用其对实践棋牌室游戏的案例进行优化过程。

    以上就是golang并发之使用sync.Pool优化性能的详细内容,更多关于go sync.Pool的资料请关注编程客栈(www.devze.com)其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜