Go编程库Sync.Pool用法示例详解

目录
  • 场景
  • 用法
    • 创建
    • GET & PUT
  • 优化 Log 函数
  • 性能测试

场景

go 如果频繁地创建、销毁对象(比如 http 服务的 json 对象,日志内容等),会对 GC 造成压力。比如下面的 Log 函数,在高并发情况下,需要频繁地创建和销毁 buffer。

func Log(w io.Writer, key, val string) {
	b := new(bytes.Buffer)
  // 按一定的格式打印日志,这一段不是重点
	b.WriteString(time.Now().UTC().Format(time.RFC3339))
	b.WriteByte(' ')
	b.WriteString(key)
	b.WriteByte('=')
	b.WriteString(val)
	b.WriteByte('\n')
	w.Write(b.Bytes())
}

这时候可以考虑复用这些 buffer。我们可以维护一个 buffer 的对象池,需要的时候从对象池拿 buffer,用完放回对象池。这时候就推荐使用 sync.Pool 了。

sync.Pool 维护着一组对象池,需要时从对象池拿对象,不需要放回对象池就可以了。它有这些特点:

  • 忙时会自动扩容对象池,闲时会自动缩容;
  • 线程安全;
  • 对象池的对象,会未经通知地自动删除;
  • 不能被 copy。

用法

创建

初始化时指定 New 方法。sync.Pool 会通过 New 方法创建对象池的对象。一般返回一个指针。

// 从对象池里取 buffer 时,如果池里没 buffer了,则调用 New 创建一个新的。
var bufPool = sync.Pool{
	New: func() interface{} {
		return new(bytes.Buffer)
	},
}

GET & PUT

通过 Get 获取对象池的对象。当使用完毕,通过 Put 把对象返回对象池。

  b := bufPool.Get().(*bytes.Buffer)  // 从对象池拿 buffer 对象
  // 操作对象,这个不重要
	b.Reset()
	b.WriteString(time.Now().UTC().Format(time.RFC3339))
  // 操作完放回对象池
	bufPool.Put(b)

优化 Log 函数

Log 函数可以使用 sync.Pool 的优化,代码如下:

var bufPool = sync.Pool{
	New: func() interface{} {
		return new(bytes.Buffer)
	},
}
func LogWithPool(w io.Writer, key, val string) {
  // 从对象池拿 buffer
	b := bufPool.Get().(*bytes.Buffer)
	b.Reset()
  // 按一定的格式打印日志,这一段不是重点
	b.WriteString(time.Now().UTC().Format(time.RFC3339))
	b.WriteByte(' ')
	b.WriteString(key)
	b.WriteByte('=')
	b.WriteString(val)
	b.WriteByte('\n')
	w.Write(b.Bytes())
  // 放回对象池
	bufPool.Put(b)
}

性能测试

我们对两个函数进行性能测试

// 不使用 sync.Pool
func BenchmarkLog(b *testing.B) {
	writer := os.NewFile(0, os.DevNull)
	for n := 0; n < b.N; n++ {
		Log(writer, "path", "/search?a=flowers")
	}
}
// 使用 sync.Pool 复用
func BenchmarkLogWithPool(b *testing.B) {
	writer := os.NewFile(0, os.DevNull)
	for n := 0; n < b.N; n++ {
		LogWithPool(writer, "path", "/search?a=flowers")
	}
}

结果:

> go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: example/pool
cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
BenchmarkLog-8                   1849365               635.0 ns/op           112 B/op          2 allocs/op
BenchmarkLogWithPool-8           1993304               614.4 ns/op            48 B/op          1 allocs/op
PASS
ok      example/pool    4.333s

相比之下,使用 Sync.Pool 和不使用的时候,内存的使用比为 48:112,优化效果还是挺明显的。

参考:

[1]. pkg.go.dev/sync#Pool

以上就是Go编程库Sync.Pool用法示例详解的详细内容,更多关于Go库Sync.Pool的资料请关注我们其它相关文章!

(0)

相关推荐

  • 深入Golang中的sync.Pool详解

    我们通常用golang来构建高并发场景下的应用,但是由于golang内建的GC机制会影响应用的性能,为了减少GC,golang提供了对象重用的机制,也就是sync.Pool对象池. sync.Pool是可伸缩的,并发安全的.其大小仅受限于内存的大小,可以被看作是一个存放可重用对象的值的容器. 设计的目的是存放已经分配的但是暂时不用的对象,在需要用到的时候直接从pool中取. 任何存放区其中的值可以在任何时候被删除而不通知,在高负载下可以动态的扩容,在不活跃时对象池会收缩. sync.Pool首先

  • GoFrame基于性能测试得知grpool使用场景

    目录 前言摘要 先说结论 测试性能代码 运行结果 总结 前言摘要 之前写了一篇 grpool goroutine池详解 | 协程管理 收到了大家积极的反馈,今天这篇来做一下grpool的性能测试分析,让大家更好的了解什么场景下使用grpool比较好. 先说结论 grpool相比于goroutine更节省内存,但是耗时更长: 原因也很简单:grpool复用了协程,减少了协程的创建和销毁,减少了内存消耗:也因为协程的复用,总的goroutine数量更少,导致耗时更多. 测试性能代码 开启for循环,

  • go 对象池化组件 bytebufferpool使用详解

    目录 1. 针对问题 2. 使用方法 3. 源码剖析 1. 针对问题 在编程开发的过程中,我们经常会有创建同类对象的场景,这样的操作可能会对性能产生影响,一个比较常见的做法是使用对象池,需要创建对象的时候,我们先从对象池中查找,如果有空闲对象,则从对象池中移除这个对象并将其返回给调用者使用,只有在池中无空闲对象的时候,才会真正创建一个新对象 另一方面,对于使用完的对象,我们并不会对它进行销毁,而是将它放回到对象池以供后续使用,使用对象池在频繁创建和销毁对象的情况下,能大幅的提升性能,同时为了避免

  • Golang WorkerPool线程池并发模式示例详解

    目录 正文 处理CVS文件记录 获取测试数据 线程池耗时差异 正文 Worker Pools 线程池是一种并发模式.该模式中维护了固定数量的多个工作器,这些工作器等待着管理者分配可并发执行的任务.该模式避免了短时间任务创建和销毁线程的代价. 在 golang 中,我们使用 goroutine 和 channel 来构建这种模式.工作器 worker 由一个 goroutine 定义,该 goroutine 通过 channel 获取数据. 处理CVS文件记录 接下来让我们通过一个例子,来进一步理

  • 深度解密 Go 语言中的 sync.Pool

    最近在工作中碰到了 GC 的问题:项目中大量重复地创建许多对象,造成 GC 的工作量巨大,CPU 频繁掉底.准备使用 sync.Pool 来缓存对象,减轻 GC 的消耗.为了用起来更顺畅,我特地研究了一番,形成此文.本文从使用到源码解析,循序渐进,一一道来. 是什么 sync.Pool 是 sync 包下的一个组件,可以作为保存临时取还对象的一个"池子".个人觉得它的名字有一定的误导性,因为 Pool 里装的对象可以被无通知地被回收,可能 sync.Cache 是一个更合适的名字. 有

  • Golang之sync.Pool使用详解

    前言 我们通常用 Golang 来开发并构建高并发场景下的服务,但是由于 Golang 内建的GC机制多少会影响服务的性能,因此,为了减少频繁GC,Golang提供了对象重用的机制,也就是使用sync.Pool构建对象池. sync.Pool介绍 首先sync.Pool是可伸缩的临时对象池,也是并发安全的.其可伸缩的大小会受限于内存的大小,可以理解为是一个存放可重用对象的容器.sync.Pool设计的目的就是用于存放已经分配的但是暂时又不用的对象,而且在需要用到的时候,可以直接从该pool中取.

  • Go编程库Sync.Pool用法示例详解

    目录 场景 用法 创建 GET & PUT 优化 Log 函数 性能测试 场景 go 如果频繁地创建.销毁对象(比如 http 服务的 json 对象,日志内容等),会对 GC 造成压力.比如下面的 Log 函数,在高并发情况下,需要频繁地创建和销毁 buffer. func Log(w io.Writer, key, val string) { b := new(bytes.Buffer) // 按一定的格式打印日志,这一段不是重点 b.WriteString(time.Now().UTC()

  • vue 之 .sync 修饰符示例详解

    在一些情况下,我们可能会需要对一个 prop (父子组件传递数据的属性) 进行"双向绑定". 在vue 1.x 中的 .sync 修饰符所提供的功能.当一个子组件改变了一个带 .sync 的prop的值时,这个变化也会同步到父组件中所绑定的值. 这很方便,但也会导致问题,因为它破坏了单向数据流.(数据自上而下流,事件自下而上走) 由于子组件改变 prop 的代码和普通的状体改动代码毫无区别,所以当你光看子组件的代码时,你完全不知道它合适悄悄地改变了父组件的状态. 这在 debug 复杂

  • Go语言基础go接口用法示例详解

    目录 概述 语法 定义接口 实现接口 空接口 接口的组合 总结 概述 Go 语言中的接口就是方法签名的集合,接口只有声明,没有实现,不包含变量. 语法 定义接口 type [接口名] interface { 方法名1(参数列表) 返回值列表 方法名2(参数列表) 返回值列表 ... } 例子 type Isay interface{ sayHi() } 实现接口 例子 //定义接口的实现类 type Chinese struct{} //实现接口 func (_ *Chinese) sayHi(

  • Go语言基础类型及常量用法示例详解

    目录 基础类型 概述 按类别有以下几种数据类型 数值类型 派生类型 变量 概述 单个变量声明 多个变量声明 基础类型 概述 在 Go 编程语言中,数据类型用于声明函数和变量.数据类型的出现时为了把数据分成所需要用大数据的时候才需要申请大内存,这样可以充分的列用内存. 按类别有以下几种数据类型 数值类型 布尔型 bool:布尔型的值只可以是常量 true 或者 false,默认值为 false. 字符串类型 string:编码统一为 UTF-8 编码标识 Unicode 文本,默认值为空字符串.

  • 无UI 组件Headless框架逻辑原理用法示例详解

    目录 概述 精读 总结 概述 Headless 组件即无 UI 组件,框架仅提供逻辑,UI 交给业务实现.这样带来的好处是业务有极大的 UI 自定义空间,而对框架来说,只考虑逻辑可以让自己更轻松的覆盖更多场景,满足更多开发者不同的诉求. 我们以 headlessui-tabs 为例看看它的用法,并读一读 源码. headless tabs 最简单的用法如下: import { Tab } from "@headlessui/react"; function MyTabs() { ret

  • python中前缀运算符 *和 **的用法示例详解

    这篇主要探讨 ** 和 * 前缀运算符,**在变量之前使用的*and **运算符. 一个星(*):表示接收的参数作为元组来处理 两个星(**):表示接收的参数作为字典来处理 简单示例: >>> numbers = [2, 1, 3, 4, 7] >>> more_numbers = [*numbers, 11, 18] >>> print(*more_numbers, sep=', ') 2, 1, 3, 4, 7, 11, 18 用途: 使用 * 和

  • golang中的三个点 '...'的用法示例详解

    '-' 其实是go的一种语法糖. 它的第一个用法主要是用于函数有多个不定参数的情况,可以接受多个不确定数量的参数. 第二个用法是slice可以被打散进行传递. 下面直接上例子: func test1(args ...string) { //可以接受任意个string参数 for _, v:= range args{ fmt.Println(v) } } func main(){ var strss= []string{ "qwr", "234", "yui

  • Pandas中的 transform()结合 groupby()用法示例详解

    首先,假设我们有如下餐厅数据集: import pandas as pd df = pd.DataFrame({ 'restaurant_id': [101,102,103,104,105,106,107], 'address': ['A','B','C','D', 'E', 'F', 'G'], 'city': ['London','London','London','Oxford','Oxford', 'Durham', 'Durham'], 'sales': [10,500,48,12,2

  • 支持PyTorch的einops张量操作神器用法示例详解

    目录 基础用法 高级用法 今天做visual transformer研究的时候,发现了einops这么个神兵利器,决定大肆安利一波. 先看链接:https://github.com/arogozhnikov/einops 安装: pip install einops 基础用法 einops的强项是把张量的维度操作具象化,让开发者"想出即写出".举个例子: from einops import rearrange # rearrange elements according to the

  • golang编程开发使用sort排序示例详解

    golang sort package: https://studygolang.com/articles/3360 sort 操作的对象通常是一个 slice,需要满足三个基本的接口,并且能够使用整数来索引 // A type, typically a collection, that satisfies sort.Interface can be // sorted by the routines in this package. The methods require that the /

随机推荐