go并发编程 - Pool对象重复利用和Once数据懒加载

sync.Pool

sync.Pool 是 Go 语言标准库中提供的一个对象池(Object Pool)实现,用于复用对象以减少内存分配和垃圾回收的开销。对象池可以提高性能,特别是在高并发环境下。

使用 sync.Pool 时,可以将需要复用的对象放入池中,以便后续需要时可以直接从池中获取,而不需要重新创建。当对象被使用完毕后,可以将其放回池中以便复用。

sync.Pool 的重点是重用对象,而不是限制对象的数量。池中的对象数量会根据需求动态增长和缩减,池中对象的生命周期由 GC(垃圾回收器)控制。

当从池中获取对象时,首先会尝试从池中取出复用的对象。如果池中没有可用的对象,那么将会调用 New 方法创建一个新的对象。获取对象时,应该进行类型断言(type assertion)来获取正确的对象类型。

需要注意的是,sync.Pool 并不保证对象在池中的生存时间或复用次数。在高并发环境下,对象可能会被多个 goroutine 同时获取和使用,需要确保对象的状态是安全可复用的。

值得一提的是,对 sync.Pool 的使用并不适用于所有情况。它主要适合于短时间内创建和销毁的对象,而不适用于长时间存储的对象,因为长时间存储对象可能会导致内存过度使用。

总结起来,sync.Pool 是 Go 语言标准库提供的一个对象池实现,用于复用对象以减少内存分配和垃圾回收的开销。它适用于短时间内创建和销毁的对象,并提供了一种高效的对象重用机制。

package _case

import (
	"fmt"
	"log"
	"math/rand"
	"sync"
)

func PoolCase() {
	target := "192.168.239.149"
	pool, err := GetPool(target)
	if err != nil {
		log.Fatal(err)
	}

	//创建连接池后,先放入10个连接
	for i := 0; i <= 10; i++ {
		coon := &Conn{
			ID:     int64(i),
			Target: target,
			Status: ON,
		}
		pool.Put(coon)
	}

	wg := sync.WaitGroup{}
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := 0; j < 5; j++ {
				conn := pool.Get()
				fmt.Println(conn.ID)
				pool.Put(conn)
			}
		}()
	}
}

连接池

const (
	ON  = 1
	OFF = 0
)

// Conn 链接池
type Conn struct {
	ID     int64
	Target string
	Status int
}

func (c *Conn) GetStatus() int {
	return c.Status
}

func NewConn(target string) *Conn {
	return &Conn{
		ID:     rand.Int63(),
		Target: target,
		Status: ON,
	}
}

ConnPool 创建Pool对象

// ConnPool 创建Pool对象
type ConnPool struct {
	sync.Pool
}

GetPool 在链接池取出

// GetPool 在链接池取出
func GetPool(target string) (*ConnPool, error) {
	return &ConnPool{
		Pool: sync.Pool{
			New: func() any { //初始化连接
				return NewConn(target)
			},
		},
	}, nil
}

Get 获取连接池,返回连接

// Get 获取连接池,返回连接
func (c *ConnPool) Get() *Conn {
	conn := c.Pool.Get().(*Conn)
	if conn.GetStatus() == OFF { //如果连接池被关闭
		conn = c.Pool.New().(*Conn)
	}
	return conn
}

Put 释放连接

// Put 释放连接
func (c *ConnPool) Put(conn *Conn) {
	if conn.GetStatus() == OFF {
		return
	}

	c.Pool.Put(conn)
}

注意事项

  1. 用户缓存一些创建成本较高, 使用比较频繁的对象
  2. Pool的长度默认为机器CPU线程数
  3. 存储在Pool中的对象随时都可能在不被通知的情况下回收
  4. 没有什么创建成本的对象不建议使用对象池

sync.Once

sync.Once 是 Go 语言标准库中提供的一种同步原语,用于保证某个操作只执行一次。通常用于在多个 goroutine 之间共享一个全局或者一次性初始化的资源。

sync.Once 类型具有一个 Do 方法,该方法接收一个函数作为参数并执行该函数。第一次调用 Do 方法时,会执行传入的函数;而后续的调用则会被忽略,不再执行该函数。

sync.Once 内部通过一个互斥锁和一个标志位实现。在第一次调用 Do 方法时,goroutine 会获取到互斥锁并检查标志位。如果标志位为 false,表示该操作尚未执行,则执行传入的函数并将标志位设置为 true;如果标志位为 true,则直接跳过函数的执行。无论是函数执行还是跳过,最后都会释放互斥锁,并且后续的调用都不会再获取互斥锁。

sync.Once 的典型用途是进行一次性资源初始化。例如,可以使用 sync.Once 来确保只有一个 goroutine 对某个全局变量进行初始化,而后续的读取操作可以直接使用该已初始化的值,而无需重复初始化。

需要注意的是,被传入 Do 方法的函数可能会被调用多次,但是 sync.Once 会确保只有第一次调用会执行实际的函数体。

总结而言,sync.Once 是 Go 语言标准库提供的一种同步原语,用于保证某个操作只执行一次。它适用于在多个 goroutine 之间共享全局或一次性初始化的资源,并提供了一种高效的初始化机制
使用sync.Once函数只被调用一次

package _case

import (
	"fmt"
	"sync"
)

type onceMap struct {
	sync.Once
	data map[string]int
}

func (m *onceMap) LoadData() {
	m.Do(func() {
		list := []string{"A", "B", "C", "D"}
		for _, item := range list {
			_, ok := m.data[item]
			if !ok {
				m.data[item] = 0
			}
			m.data[item] += 1
		}
	}) //只调用一次该函数
}

调用10次函数只得到一次结果

func OnceCase() {
	o := &onceMap{
		data: make(map[string]int),
	}
	wg := sync.WaitGroup{}

	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			o.LoadData()
		}()
	}
	wg.Wait()
	fmt.Println(o.data)	//map[A:1 B:1 C:1 D:1]
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值