声明:本文是《Go并发编程实战》的样章,感谢图灵授权并发编程网站发布样章,禁止以任何形式转载此文。

本章要讲解的是sync.Pool类型。我们可以把sync.Pool类型值看作是存放可被重复使用的值的容器。此类容器是自动伸缩的、高效的,同时也是并发安全的。为了描述方便,我们也会把sync.Pool类型的值称为临时对象池,而把存于其中的值称为对象值。至于为什么要加“临时“这两个字,我们稍后再解释。
我们在用复合字面量初始化一个临时对象池的时候可以为它唯一的公开字段New赋值。该字段的类型是func() interface{},即一个函数类型。可以猜到,被赋给字段New的函数会被临时对象池用来创建对象值。不过,实际上,该函数几乎仅在池中无可用对象值的时候才会被调用。
类型sync.Pool有两个公开的方法。一个是Get,另一个是Put。前者的功能是从池中获取一个interface{}类型的值,而后者的作用则是把一个interface{}类型的值放置于池中。

通过Get方法获取到的值是任意的。如果一个临时对象池的Put方法未被调用过,且它的New字段也未曾被赋予一个非nil的函数值,那么它的Get方法返回的结果值就一定会是nil。我们稍后会讲到,Get方法返回的不一定就是存在于池中的值。不过,如果这个结果值是池中的,那么在该方法返回它之前就一定会把它从池中删除掉。
这样一个临时对象池在功能上看似与一个通用的缓存池相差无几。但是实际上,临时对象池本身的特性决定了它是一个“个性”非常鲜明的同步工具。我们在这里说明它的两个非常突出的特性。
第一个特性是,临时对象池可以把由其中的对象值产生的存储压力进行分摊。更进一步说,它会专门为每一个与操作它的Goroutine相关联的P都生成一个本地池。在临时对象池的Get方法被调用的时候,它一般会先尝试从与本地P对应的那个本地池中获取一个对象值。如果获取失败,它就会试图从其他P的本地池中偷一个对象值并直接返回给调用方。如果依然未果,那它只能把希望寄托于当前的临时对象池的New字段代表的那个对象值生成函数了。注意,这个对象值生成函数产生的对象值永远不会被放置到池中。它会被直接返回给调用方。另一方面,临时对象池的Put方法会把它的参数值存放到与当前P对应的那个本地池中。每个P的本地池中的绝大多数对象值都是被同一个临时对象池中的所有本地池所共享的。也就是说,它们随时可能会被偷走。
临时对象池的第二个突出特性是对垃圾回收友好。垃圾回收的执行一般会使临时对象池中的对象值被全部移除。也就是说,即使我们永远不会显式的从临时对象池取走某一个对象值,该对象值也不会永远待在临时对象池中。它的生命周期取决于垃圾回收任务下一次的执行时间。
请读者阅读一下这段代码:

package mainimport ("fmt""runtime""runtime/debug""sync""sync/atomic"
)func main() {// 禁用GC,并保证在main函数执行结束前恢复GCdefer debug.SetGCPercent(debug.SetGCPercent(-1))var count int32newFunc := func() interface{} {return atomic.AddInt32(&count, 1)}pool := sync.Pool{New: newFunc}// New 字段值的作用v1 := pool.Get()fmt.Printf("v1: %v\n", v1)// 临时对象池的存取pool.Put(newFunc())pool.Put(newFunc())pool.Put(newFunc())v2 := pool.Get()fmt.Printf("v2: %v\n", v2)// 垃圾回收对临时对象池的影响debug.SetGCPercent(100)runtime.GC()v3 := pool.Get()fmt.Printf("v3: %v\n", v3)pool.New = nilv4 := pool.Get()fmt.Printf("v4: %v\n", v4)
}

在这里,我们使用runtime/debug代码包的SetGCPercent函数来禁用、恢复GC以及指定垃圾收集比率(详见第7章的第1节中的相关说明),以保证我们的演示能够如愿进行。
我们把这段代码存放在gocp项目的sync1/pool代码包的文件pool_demo.go中,并使用go run命令运行它。就像下面这样:
hc@ubt:~/golang/goc2p/src/sync1/pool$ go run pool_demo.go
而后,我们会在标准输出上看到如下内容:

v1: 1
v2: 2
v3: 5
v4: <nil>

请读者注意第3行和第4行的内容,也就是我们在手动的进行垃圾回收之后的输出内容。在把nil赋给pool的New字段之前,即使手动的执行了垃圾回收,我们也是可以从临时对象池获取到一个对象值的。而在这之后,我们却只能取出nil。读者应该可以依据我们刚刚描述的那两个特性想明白如此输出的原因。
看到这里,读者可能会隐约的感觉到,我们在使用临时对象池的时候应该依照一些方式方法,否则就会很容易迈入陷坑。实际情况确实如此。
首先,我们不能对通过Get方法获取到的对象值有任何假设。到底哪一个值会被取出是完全不确定的。这是因为我们总是不能得知操作临时对象池的Goroutine在哪一时刻会与哪一个P相关联,尤其是在比上述示例更加复杂的程序的运行过程中。在这种情况下,我们也就无从知晓我们放入的对象值会被存放到哪一个本地池中,以及哪一个Goroutine执行的Get方法会返回该对象值。所以,我们给予临时对象池的对象值生成函数所产生的值以及通过调用它的Put方法放入到池中的值都应该是无状态的或者状态一致的。从另一方面说,我们在取出并使用这些值的时候也不应该以其中的任何状态作为先决条件。这一点非常的重要。
第二个需要注意的地方实际上与我们前面讲到的第二个特性紧密相关。临时对象池中的任何对象值都有可能在任何时候被移除掉,并且根本不会通知该池的使用方。这种情况常常会发生在垃圾回收器即将开始回收内存垃圾的时候。如果这时临时对象池中的某个对象值仅被该池引用,那么它还可能会在垃圾回收的时候被回收掉。因此,我们也就不能假设之前放入到临时对象池的某个对象值会一直待在池中,即使我们没有显式的把它从池中取出。甚至一个对象值可以在临时对象池中待多久,我们也无法假设。除非我们像前面的示例那样手动的控制GC的启停。不过,我们并不推荐这种方式。这会带来一些其他问题。
依据我们刚刚讲述的临时对象池特性和使用注意事项,读者应该可以想象得出临时对象池的一些适用场景(比如作为临时且状态无关的数据的暂存处),以及一些不适用的场景(比如用来存放数据库连接的实例)。如果我们在做实现技术的选型的时候把临时对象池作为了候选之一,那么就应该好好想想它的“个性”是不是符合你的需要。如果真的适合,那么它的特性一定会为你的程序增光添彩,无论在功能上还是在性能上。而如果它被用在了不恰当的地方,那么就只能适得其反了。

《GO并发编程实战》—— 临时对象池相关推荐

  1. JAVA并发编程实战——共享对象

    目录 思维导图 1. 可见性 1. 1 过期数据 1.2 锁和可见性 1.3 Volatile变量 2. 发布和逸出 2.1 安全构建实践 3. 线程封闭 3.1 栈限制 3.2 ThreadLoca ...

  2. [Java并发编程实战] 共享对象之可见性

    2019独角兽企业重金招聘Python工程师标准>>> 「 ***盛年不重来,一日难再晨,及时当勉励,岁月不待人.***」 陶渊明 PS: 如果觉得本文有用的话,请帮忙点赞,留言评论 ...

  3. Java并发编程实战笔记2:对象的组合

    设计线程安全的类 在设计现车让安全类的过程之中,需要包含以下三步: 找出构成对象状态的所有变量 找出约束状态变量的不变性条件 建立对象状态的并发访问策略 实例封闭 通过封闭机制与合适的加锁策略结合起来 ...

  4. java单线程共享,「Java并发编程实战」之对象的共享

    前言 本系列博客是对<Java并发编程实战>的一点总结,本篇主要讲解以下几个内容,内容会比较枯燥.可能大家看标题不能能直观的感受出到底什么意思,这就是专业术语,哈哈,解释下,术语(term ...

  5. 《.NET并发编程实战》之 函数式并发基础

    本书是一本划时代的著作!在以下领域具有非常深远的意义: .NET 开发领域--本书再次将.NET 开发人员进行了分层隔代. 高性能/多核/并发编程领域--本书让锁从此变成过去时. 程序员职业生涯领域- ...

  6. Java并发编程实战————Executor框架与任务执行

    引言 本篇博客介绍通过"执行任务"的机制来设计应用程序时需要掌握的一些知识.所有的内容均提炼自<Java并发编程实战>中第六章的内容. 大多数并发应用程序都是围绕&qu ...

  7. Java并发编程实战_不愧是领军人物!这种等级的“Java并发编程宝典”谁能撰写?...

    前言 大家都知道并发编程技术就是在同一个处理器上同时的去处理多个任务,充分的利用到处理器的每个核心,最大化的发挥处理器的峰值性能,这样就可以避免我们因为性能而产生的一些问题. 大厂的核心负载肯定是非常 ...

  8. 视频教程-Java并发编程实战-Java

    Java并发编程实战 2018年以超过十倍的年业绩增长速度,从中高端IT技术在线教育行业中脱颖而出,成为在线教育领域一匹令人瞩目的黑马.咕泡学院以教学培养.职业规划为核心,旨在帮助学员提升技术技能,加 ...

  9. Go并发编程实战 (郝林 著)

    第1章 初识Go语言 1.1 语言特性 1.2 安装和设置 1.3 工程构造 1.3.1 工作区 1.3.2 GOPATH 1.3.3 源码文件 package mainimport ("f ...

  10. 【资源分享】Go并发编程实战(第2版)郝林 著 PDF 下载

    文章目录 一.下载链接 二.内容简介 三.作者简介 四.目录 一.下载链接 废话少说,先上链接: 点此下载 https://download.csdn.net/download/tmt123421/1 ...

最新文章

  1. 第k大 or 第k小 or 中位数
  2. SQL SERVER 取所有表及注释 和 字段属性
  3. Qt学习笔记之QTranslator
  4. mpvue 从零开始 女友的收纳盒 6 mpvue-entry入口管理
  5. mysql 51cto 数据类型_Mysql支持的数据类型
  6. java n%9==0_用C++实现求N!中末尾0的个数的方法详解
  7. centos mysql5.7主从同步配置_centos 7 配置 mysql 5.7 主从复制
  8. Padavan 路由器系统如何放开wan口的samba访问
  9. CodeForces - 379E New Year Tree Decorations(暴力卡精度)
  10. Class文件格式总结
  11. 伦敦大学国王学院计算机学院官网,伦敦大学国王学院 King’s College London
  12. 广告设计、海报、宣传单、易拉宝、照片放大、网站设计;
  13. javaSE探赜索隐之三<类与对象的爱恨情仇中>
  14. R语言ggplot2可视化:使用ggpubr包的arrangeGrob函数将多个可视化结果整合为gtable对象、使用as_ggplot函数将gtable对象转化为ggplot对象
  15. 微信提醒事项功能怎么设置
  16. RabbitMQ中重试机制的坑
  17. 2021-09-12 Autodesk inventor 技巧整理
  18. 【无标题】五大免费使用的在线客服系统盘点
  19. 下载Xshell6和Xftp6
  20. SCT9330STER,芯洲3.8V-30V输入,3A降压DCDC,替代TPS54331

热门文章

  1. 简单易懂——Dijkstra算法讲解
  2. redis在window下的启动
  3. 逻辑回归(Logistic Regression)学习笔记
  4. php中表单名称未定义,php – zf2,表单集合没有在zf2中创建正确的输入名称
  5. Android TextView 显示圆圈背景或者设置圆角、圆形
  6. Android 自动轮播图+滑动效果
  7. 期末考试、考研、学技术
  8. Vue2.0组织浏览器返回事件
  9. poj 1503 Integer Inquiry (高精度运算)
  10. 基于ffmpeg的kxmovie的使用