一文详解Go语言中对象池的正确打开方式

 更新时间:2025年02月07日 08:18:31   作者:烛阴  
对象池是一种设计模式,它维护一组已经创建好的对象,当需要使用对象时,直接从对象池中获取,使用完毕后再放回对象池,而不是频繁地创建和销毁对象,下面我们就来看看Go语言中对象池的具体使用吧

什么是对象池

对象池是一种设计模式,它维护一组已经创建好的对象,当需要使用对象时,直接从对象池中获取,使用完毕后再放回对象池,而不是频繁地创建和销毁对象。 这样可以显著减少 GC 的压力,提高程序的性能。

为什么不用 sync.Pool

sync.Pool 是 Go 标准库提供的对象池实现,但它有一些限制:

  • GC 不确定性: sync.Pool 中的对象可能会被 GC 回收,导致每次获取对象都需要重新创建,失去了对象池的意义。
  • 适用场景有限: sync.Pool 更适合于临时对象的复用,对于需要长期存在的对象,效果不佳。
  • 控制力不足: 无法精确控制对象池的大小和对象的生命周期。

因此,在某些场景下,我们需要自定义对象池,以获得更高的性能和控制力。

手撸对象池:原理与实现

下面,我们就来手撸一个简单的对象池,并分析其原理。

1. 定义对象池结构体

package main

import (
	"errors"
	"fmt"
	"sync"
	"time"
)

type Pool struct {
	objects chan interface{}   // 使用 channel 存储对象
	factory func() interface{} // 创建对象的工厂函数
	mu      sync.Mutex         // 保护对象池
}

var g_index int = 0

func NewPool(size int, factory func() interface{}) *Pool {
	if size <= 0 {
		panic("对象池大小必须大于 0")
	}
	pool := make(chan interface{}, size)
	for i := 0; i < size; i++ {
		pool <- factory() // 预先创建对象并放入对象池
	}
	return &Pool{
		objects: pool,
		factory: factory,
	}
}

func (p *Pool) Get() interface{} {
	select {
	case obj := <-p.objects:
		return obj // 从对象池中获取对象
	default:
		// 对象池为空,创建新对象
		fmt.Println("create new object")
		p.mu.Lock()
		defer p.mu.Unlock()
		return p.factory()
	}
}

func (p *Pool) Put(obj interface{}) error {
	select {
	case p.objects <- obj: // 对象放回对象池
		return nil
	default:
		// 对象池已满,丢弃对象
		obj2 := obj.(*MyObject)
		fmt.Println("pool is full, discard object", obj2.index)
		obj = nil
		return errors.New("pool is full")
	}
}

func (p *Pool) Len() int {
	return len(p.objects)
}

type MyObject struct {
	Data  string
	index int
}

func main() {
	// 创建对象工厂
	objectFactory := func() interface{} {
		g_index += 1
		return &MyObject{Data: "Initial Data", index: g_index}
	}

	// 创建对象池,大小为 10
	pool := NewPool(10, objectFactory)

	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(idx int) {
			fmt.Println("pool len:", pool.Len())
			obj := pool.Get().(*MyObject)
			defer func() {
				wg.Done()
				pool.Put(obj)
			}()
			fmt.Println("from :", obj.Data, obj.index, idx)
			time.Sleep(time.Millisecond * 2) // 模拟一些工作
		}(i)
	}
	wg.Wait()
}

代码解释:

  • ObjectPool 结构体包含一个 pool channel,用于存储对象。
  • factory 是一个函数,用于创建新的对象。
  • NewObjectPool 函数用于创建对象池,并预先创建指定数量的对象放入对象池。
  • Get 函数用于从对象池中获取对象。如果对象池为空,则调用 factory 创建新的对象。
  • Put 函数用于将对象放回对象池。如果对象池已满,则丢弃对象。
  • 定义了一个 MyObject 结构体,作为对象池中存储的对象类型。
  • 创建一个 objectFactory 函数,用于创建 MyObject 对象。
  • 创建一个大小为 10 的对象池,并传入 objectFactory 函数。
  • 从对象池中获取对象,修改对象的数据,然后将对象放回对象池。
  • 再次从对象池中获取对象,可以看到对象的数据已经被修改,说明对象被成功复用。

总结

通过手撸对象池,我们不仅可以更好地理解对象池的原理,还可以根据实际需求定制对象池,以获得更高的性能和控制力。 在需要频繁创建和销毁对象的场景下,使用对象池可以显著提高程序的性能,告别 GC 噩梦!

到此这篇关于一文详解Go语言中对象池的正确打开方式的文章就介绍到这了,更多相关Go对象池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go 语言入门学习之时间包

    Go 语言入门学习之时间包

    这篇文章主要介绍了Go 语言入门学习之时间包,GO 语言提供了 ​​time​​包来测量和显示时间,下文关于GO时间包的相关介绍需要的小伙伴可以参考一下
    2022-04-04
  • Golang中Delve版本太低无法Debug的问题

    Golang中Delve版本太低无法Debug的问题

    这篇文章主要介绍了Golang中Delve版本太低无法Debug的问题,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • Golang Cron 定时任务的实现示例

    Golang Cron 定时任务的实现示例

    这篇文章主要介绍了Golang Cron 定时任务的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • 举例详解Go语言中os库的常用函数用法

    举例详解Go语言中os库的常用函数用法

    这篇文章主要介绍了Go语言中os库的常用函数用法,os函数的使用是Go语言入门学习中的基础知识,需要的朋友可以参考下
    2015-10-10
  • 深入讲解Go语言中函数new与make的使用和区别

    深入讲解Go语言中函数new与make的使用和区别

    大家都知道Go语言中的函数new与函数make一直是新手比较容易混淆的东西,看着相似,但其实不同,不过解释两者之间的不同也非常容易,下面这篇文章主要给大家介绍了关于Go语言中函数new与make区别的相关资料,需要的朋友可以参考下。
    2017-10-10
  • golang pprof 监控goroutine thread统计原理详解

    golang pprof 监控goroutine thread统计原理详解

    这篇文章主要为大家介绍了golang pprof 监控goroutine thread统计原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • golang语法使用的注意事项

    golang语法使用的注意事项

    这篇文章主要给大家介绍了关于golang语法使用的一些注意事项,Golang是一种静态类型的编程语言,它支持基本的数据类型,包括整型、浮点型、布尔型、字符串型,需要的朋友可以参考下
    2023-07-07
  • golang之反射和断言的具体使用

    golang之反射和断言的具体使用

    这篇文章主要介绍了golang之反射和断言的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Golang JSON的进阶用法实例讲解

    Golang JSON的进阶用法实例讲解

    这篇文章主要给大家介绍了关于Golang JSON进阶用法的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用golang具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-09-09
  • 让go程序以后台进程或daemon方式运行方法探究

    让go程序以后台进程或daemon方式运行方法探究

    本文探讨了如何通过Go代码实现在后台运行的程序,最近我用Go语言开发了一个WebSocket服务,我希望它能在后台运行,并在异常退出时自动重新启动,我的整体思路是将程序转为后台进程,也就是守护进程(daemon)
    2024-01-01

最新评论