当前位置:首页 > 文章列表 > Golang > Go教程 > Go sync.Pool.New 怎么避免 Get 返回 nil

Go sync.Pool.New 怎么避免 Get 返回 nil

来源:17golang原创 2026-10-06 23:16:39 0浏览 收藏

我第一次用 sync.Pool 缓存 bytes.Buffer 时,直接写了 pool.Get().(*bytes.Buffer)。只要池还是空的,这段代码就可能因为 Get 返回 nil 而触发类型断言 panic。解决办法很明确:在 Pool 对外使用前设置 New,并保证它每次都返回同一种非 nil 具体指针。

官方文档:https://pkg.go.dev/sync

要点速览
  • Get 在原本要返回 nil 且 New 非 nil 时,返回调用 New 的结果。
  • New 自己返回 nil 或 typed nil,调用方仍可能拿到不可用值。
  • Pool 中的对象可被运行时随时移除,不能靠预先 Put 一个对象来保证以后永不为空。

先把保证边界说清楚

sync.Pool 保存的是可临时复用的对象。官方文档明确说明:池内项目可能在没有通知的情况下自动移除,Get 也可能忽略已有对象,把池当成空池。因此,我不会把 Pool 当成“至少保留一个对象”的容器,也不会用它管理必须关闭、必须归还或创建可能失败的连接。

真正能避免空池返回 nil 的机制是 New:当 Get 否则将返回 nil,且 New 已设置时,Get 会调用它。这个保证还有一个前提——工厂返回值本身必须可用。

sync.Pool、New、Get、Put、调用方与临时对象之间的静态契约关系图
图1:Pool 配置、借用与归还边界的静态说明图,不是运行截图。

在并发使用前固定 New

下面是我现在常用的最小写法。工厂直接返回指针,既符合官方示例的建议,也让调用方能统一断言为 *bytes.Buffer:

package bufferpool

import (
	"bytes"
	"sync"
)

var buffers = sync.Pool{
	New: func() any {
		// 工厂必须返回非 nil,且返回类型要与 Get 处的断言一致。
		return new(bytes.Buffer)
	},
}

func acquire() *bytes.Buffer {
	// New 已在并发访问前固定,因此空池时也会得到可用指针。
	b := buffers.Get().(*bytes.Buffer)
	b.Reset()
	return b
}

New 不能在其他 goroutine 正调用 Get 时修改。工程里最好在包级变量初始化或构造阶段一次设置完成,之后只读。工厂还应保持无共享状态或自行保证并发安全,因为多个 goroutine 可能同时遇到池空。

把归还规则放进同一组函数

只设置 New 还不够。复用对象可能携带上一次调用留下的数据,甚至因为一次大请求扩容得很大。我倾向于把 Pool 保持为未导出变量,只暴露借用和归还函数:

const maxBufferCapacity = 64  maxBufferCapacity {
		return
	}

	// 清掉可见内容后再归还,下个调用方不会读到旧数据。
	b.Reset()
	buffers.Put(b)
}

这里的容量阈值只是策略示例,应按业务对象大小分布调整。关键不是固定数字,而是把“允许归还什么类型、归还前如何清理、什么对象直接丢弃”集中到一个入口。

typed nil 是最容易漏掉的陷阱

接口是否为 nil,取决于动态类型和值是否都为空。一个装进 any 的 nil 指针带有动态类型,因此接口本身不等于 nil,但断言后得到的指针仍然是 nil:

var bad = sync.Pool{
	New: func() any {
		// 错误示例:返回值接口带有 *bytes.Buffer 类型,但内部指针仍为 nil。
		var b *bytes.Buffer
		return b
	},
}

func badAcquire() *bytes.Buffer {
	// 类型断言会成功,但结果为 nil,后续调用 b.Reset() 仍会 panic。
	return bad.Get().(*bytes.Buffer)
}

因此不要只检查 New != nil,还要让工厂直接构造非 nil 值,例如 new(bytes.Buffer) 或 &bytes.Buffer{}。同样,不要把别的类型放进这个 Pool;否则下一次 Get 可能在类型断言处 panic。

sync.Pool 中非 nil 指针、typed nil、类型断言、Reset 与容量边界的静态关系图
图2:工厂返回值、类型边界与复用卫生的静态说明图,不是运行结果。

用测试冻结非 nil 契约

Pool 的目标不是保证复用命中,而是即使命中失败也能安全得到新对象。测试时不要断言两次 Get 必须拿到同一指针;应该断言借用结果始终非 nil、状态已清空,并确认并发调用不会暴露 nil:

package bufferpool

import (
	"sync"
	"testing"
)

func TestAcquireNeverReturnsNil(t *testing.T) {
	const workers = 32
	var wg sync.WaitGroup
	wg.Add(workers)

	for i := 0; i 

如果对象创建会返回错误,例如网络连接、文件句柄或外部资源,就不要把失败隐藏在 Pool.New 里。New 没有 error 返回位,这类资源需要显式构造、健康检查和关闭流程。

常见误区与推荐处理

做法问题推荐处理
只提前 Put 一个对象对象可能被自动移除,Get 也可能忽略池设置返回非 nil 值的 New
New 返回 typed nil接口非 nil,但断言后的指针仍为 nil直接 return new(T)
并发期间修改 New违反 Pool 的使用约束初始化时固定,之后不再修改
多个类型混用一个 Pool类型断言可能 panic每种类型独立封装 Pool
依赖复用命中率做正确性判断Pool 不保证保存或返回已 Put 对象把 Pool 仅作为性能优化

速查清单

  • New 在任何并发 Get 前设置完成。
  • New 返回固定类型的非 nil 指针。
  • Pool 未导出,外部不能混入其他类型。
  • acquire 统一类型断言与状态初始化。
  • release 负责 Reset、nil 过滤和容量上限。
  • 测试只验证公开契约,不假设对象一定被复用。

相关问题

设置了 New,Get 就一定复用旧对象吗?

不一定。Get 可以忽略池中对象,池内对象也可能被自动移除;New 只保证在原本要返回 nil 时提供一个新值。

New 可以返回结构体而不是指针吗?

语义上可以,但官方示例建议通常返回指针类型,因为指针装入接口值时可避免额外分配。更重要的是,全程保持返回与归还类型一致。

sync.Pool 适合数据库连接吗?

通常不适合。连接需要容量、健康检查、超时和关闭语义,而 sync.Pool 允许对象随时消失,更适合可丢弃、可重新创建的临时对象。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
ONNX Runtime 动态量化怎么选择需要排除的节点ONNX Runtime 动态量化怎么选择需要排除的节点
上一篇
ONNX Runtime 动态量化怎么选择需要排除的节点
CNCF 2026 项目活跃度报告透露了哪些变化
下一篇
CNCF 2026 项目活跃度报告透露了哪些变化
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    355次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    416次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    423次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    378次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    200次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码