我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Golang中的sync.Pool怎么用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Golang中的sync.Pool怎么用

这篇文章主要讲解了“Golang中的sync.Pool怎么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang中的sync.Pool怎么用”吧!

原理分析

1.1 结构依赖关系图

Golang中的sync.Pool怎么用

下面是相关源代码,不过是已经删减了对本次分析没有用的代码.

type Pool struct {    // GMP中,每一个P(协程调度器)会有一个数组,数组大小位localSize.  local     unsafe.Pointer  // p 数组大小. localSize uintptr New func() any}// poolLocal 每个P(协程调度器)的本地pool.type poolLocal struct { poolLocalInternal    // 保证一个poolLocal占用一个缓存行 pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte}type poolLocalInternal struct { private any       // Can be used only by the respective P. 16 shared  poolChain // Local P can pushHead/popHead; any P can popTail. 8}type poolChain struct { head *poolChainElt tail *poolChainElt}type poolChainElt struct { poolDequeue next, prev *poolChainElt}type poolDequeue struct { // head 高32位,tail低32位. headTail uint64 vals []eface}// 存储具体的value. type eface struct { typ, val unsafe.Pointer}

1.2 用图让代码说话

Golang中的sync.Pool怎么用

1.3 Put过程分析

Put 过程分析比较重要,因为这里会包含pool所有依赖相关分析.

总的分析学习过程可以分为下面几个步骤:

获取P对应的poolLocal

val如何进入poolLocal下面的poolDequeue队列中的.

如果当前协程获取到当前P对应的poolLocal之后进行put前,协程让出CPU使用权,再次调度过来之后,会发生什么?

读写内存优化.

数组直接操作内存,而不经过Golang

充分利用uint64值的特性,将headtail用一个值来进行表示,减少CPU访问内存次数.

获取P对应的poolLocal

sync.Pool.local其实是一个指针,并且通过变量+结构体大小来划分内存空间,从而将这片内存直接划分为数组. Go 在Put之前会先对当前Goroutine绑定到当前P中,然后通过pid获取其在local内存地址中的歧视指针,在获取时是会进行内存分配的. 具体如下:

func (p *Pool) pin() (*poolLocal, int) { // 返回运行当前协程的P(协程调度器),并且设置禁止抢占. pid := runtime_procPin() s := runtime_LoadAcquintptr(&p.localSize) // load-acquire l := p.local                              // load-consume // pid < 核心数. 默认走该逻辑. if uintptr(pid) < s {  return indexLocal(l, pid), pid } // 设置的P大于本机CPU核心数. return p.pinSlow()}// indexLocal 获取当前P的poolLocal指针. func indexLocal(l unsafe.Pointer, i int) *poolLocal { // l p.local指针开始位置. // 我猜测这里如果l为空,编译阶段会进行优化.  lp := unsafe.Pointer(uintptr(l) + uintptr(i)*unsafe.Sizeof(poolLocal{})) // uintptr真实的指针. // unsafe.Pointer Go对指针的封装: 用于指针和结构体互相转化. return (*poolLocal)(lp)}

从上面代码我们可以看到,Go通过runtime_procPin来设置当前Goroutine独占P,并且直接通过头指针+偏移量(数组结构体大小)来进行对内存划分为数组.

Put 进入poolDequeue队列:

Go在Push时,会通过headtail来获取当前队列内元素个数,如果满了,则会重新构建一个环型队列poolChainElt,并且设置为poolChain.head,并且赋值next以及prev.

通过下面代码,我们可以看到,Go通过逻辑运算判断队列是否满的设计时非常巧妙的,如果后续我们去开发组件,也是可以这么进行设计的。

func (c *poolChain) pushHead(val any) { d := c.head    // 初始化.  if d == nil {  // Initialize the chain.  const initSize = 8 // Must be a power of 2  d = new(poolChainElt)  d.vals = make([]eface, initSize)  c.head = d  // 将新构建的d赋值给tail.  storePoolChainElt(&c.tail, d) } // 入队. if d.pushHead(val) {  return } // 队列满了. newSize := len(d.vals) * 2 if newSize >= dequeueLimit {        // 队列大小默认为2的30次方.   newSize = dequeueLimit }    // 赋值链表前后节点关系.  // prev. // d2.prev=d1. // d1.next=d2. d2 := &poolChainElt{prev: d} d2.vals = make([]eface, newSize) c.head = d2 // next . storePoolChainElt(&d.next, d2) d2.pushHead(val)}// 入队poolDequeuefunc (d *poolDequeue) pushHead(val any) bool { ptrs := atomic.LoadUint64(&d.headTail) head, tail := d.unpack(ptrs) // head 表示当前有多少元素. if (tail+uint32(len(d.vals)))&(1<<dequeueBits-1) == head {  return false } // 环型队列. head&uint32(len(d.vals)-1) 表示当前元素落的位置一定在队列上. slot := &d.vals[head&uint32(len(d.vals)-1)] typ := atomic.LoadPointer(&slot.typ) if typ != nil {  return false } // The head slot is free, so we own it. if val == nil {  val = dequeueNil(nil) }    // 向slot写入指针类型为*any,并且值为val. *(*any)(unsafe.Pointer(slot)) = val    // headTail高32位++ atomic.AddUint64(&d.headTail, 1<<dequeueBits) return true}

Get实现逻辑:

其实我们看了Put相关逻辑之后,我们可能很自然的就想到了Get的逻辑,无非就是遍历链表,并且如果队列中最后一个元素不为空,则会将该元素返回,并且将该插槽赋值为空值.

感谢各位的阅读,以上就是“Golang中的sync.Pool怎么用”的内容了,经过本文的学习后,相信大家对Golang中的sync.Pool怎么用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Golang中的sync.Pool怎么用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Golang中的sync.Pool怎么用

这篇文章主要讲解了“Golang中的sync.Pool怎么用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang中的sync.Pool怎么用”吧!原理分析1.1 结构依赖关系图下面是相
2023-07-05

GoLang sync.Pool怎么使用

这篇文章主要讲解了“GoLang sync.Pool怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“GoLang sync.Pool怎么使用”吧!使用场景一句话总结:保存和复用临时对象
2023-07-04

深入浅出Golang中的sync.Pool

sync.Pool是可伸缩的,也是并发安全的,其大小仅受限于内存大小。本文主要为大家介绍一下Golang中sync.Pool的原理与使用,感兴趣的小伙伴可以了解一下
2023-03-13

Go语言中的sync.Pool怎么使用

本篇内容介绍了“Go语言中的sync.Pool怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 简介本文将介绍 Go 语言中的 s
2023-07-05

深入了解Go语言中sync.Pool的使用

本文将介绍 Go 语言中的 sync.Pool并发原语,包括sync.Pool的基本使用方法、使用注意事项等的内容,对我们了解Go语言有一定的帮助,需要的可以参考一下
2023-05-15

golang中的.()怎么使用

这篇文章主要介绍“golang中的.()怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“golang中的.()怎么使用”文章能帮助大家解决问题。什么是.()用法?在golang中,.()被称为
2023-07-05

Golang中的Mutex怎么使用

本篇内容介绍了“Golang中的Mutex怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Mutex基本概念Mutex 是 Go 语言
2023-07-05

Golang中的WaitGroups怎么使用

本篇内容介绍了“Golang中的WaitGroups怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是WaitGroupsWait
2023-07-05

Golang中的interface怎么使用

这篇文章主要讲解了“Golang中的interface怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang中的interface怎么使用”吧!万能类型interface在Jav
2023-06-27

Golang中的RWMutex怎么使用

本篇内容主要讲解“Golang中的RWMutex怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Golang中的RWMutex怎么使用”吧!RWMutex 的整体模型正如 RWMutex
2023-07-05

Golang中的sync.Cond怎么使用

本文小编为大家详细介绍“Golang中的sync.Cond怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Golang中的sync.Cond怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1. 基本
2023-07-05

Golang中的sync.Map怎么使用

今天小编给大家分享一下Golang中的sync.Map怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。map 在并发下
2023-07-05

golang中map怎么用

小编给大家分享一下golang中map怎么用,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!map:map 就是存放无序,且key不同的的集合。定义集合: make
2023-06-04

golang中select怎么用

这篇文章将为大家详细讲解有关golang中select怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是golanggolang 是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收
2023-06-15

golang中的空接口怎么用

这篇文章给大家分享的是有关golang中的空接口怎么用的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1、空接口Golang 中的接口可以不定义任何方法,没有定义任何方法的接口就是空接口。空接口表示,没有任何约束,
2023-06-14

golang中的defer函数怎么用

本文小编为大家详细介绍“golang中的defer函数怎么用”,内容详细,步骤清晰,细节处理妥当,希望这篇“golang中的defer函数怎么用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。golang的defe
2023-07-04

golang中的time模块怎么用

小编给大家分享一下golang中的time模块怎么用,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、time的常用示例打印当前时间戳fmt.Println(time.Now().Unix())# 1389058332st
2023-06-27

golang中的注释怎么使用

本篇内容主要讲解“golang中的注释怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“golang中的注释怎么使用”吧!一、单行注释单行注释是在代码行的末尾添加注释的方式,以“//”开头。
2023-07-05

golang中toolkits包怎么用

这篇文章主要介绍了golang中toolkits包怎么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在查看 open-falcon 项目源码时,经常会看到其引用了一个类库
2023-06-27

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录