一文详解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的面向对象编程

    本文主要围绕 Golang 的 Object-oriented 所展开,介绍了其基本的面向对象的基本概念及代码实战,有需要的小伙伴跟着小编一起来学习吧
    2023-04-04
  • 使用Go语言编写一个极简版的容器Container

    使用Go语言编写一个极简版的容器Container

    Docker作为一种流行的容器化技术,对于每一个程序开发者而言都具有重要性和必要性,因为容器化相关技术的普及大大简化了开发环境配置、更好的隔离性和更高的安全性,对于部署项目和团队协作而言也更加方便,本文将尝试使用Go语言编写一个极简版的容器
    2023-10-10
  • Go语言学习之时间函数使用详解

    Go语言学习之时间函数使用详解

    这篇文章主要为大家详细介绍了Go语言中时间函数的使用方法,文中的示例代码讲解详细,对我们学习Go语言有一定的帮助,需要的可以参考一下
    2022-04-04
  • golang通过cgo调用C++库源码示例

    golang通过cgo调用C++库源码示例

    这篇文章主要给大家介绍了关于golang通过cgo调用C++库的相关资料,CGO是GO语言里面的一个特性,CGO属于GOLANG的高级用法,主要是通过使用GOLANG调用CLANG实现的程序库,需要的朋友可以参考下
    2024-02-02
  • golang网络通信超时设置方式

    golang网络通信超时设置方式

    这篇文章主要介绍了golang网络通信超时设置方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang排列组合算法问题之全排列实现方法

    Golang排列组合算法问题之全排列实现方法

    这篇文章主要介绍了Golang排列组合算法问题之全排列实现方法,涉及Go语言针对字符串的遍历及排列组合相关操作技巧,需要的朋友可以参考下
    2017-01-01
  • golang基于errgroup实现并发调用的方法

    golang基于errgroup实现并发调用的方法

    这篇文章主要介绍了golang基于errgroup实现并发调用,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • go实现文件的创建、删除与读取示例代码

    go实现文件的创建、删除与读取示例代码

    这篇文章主要给大家介绍了关于go如何实现文件的创建、删除与读取的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧
    2019-02-02
  • Go基于struct tag实现结构体字段级别的访问控制

    Go基于struct tag实现结构体字段级别的访问控制

    本文将会基于这个主题展开,讨论Go中的结构体tag究竟是什么,我们该如何利用它,另外,文末还提供了一个实际案例,实现结构体字段级别的访问,帮助我们进一步提升对struct tag的理解
    2024-02-02
  • Go  import _ 下划线使用

    Go  import _ 下划线使用

    这篇文章主要为大家介绍了Go  import下划线_使用小技巧,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12

最新评论