ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Goroutine之sync.pool

2021-10-30 14:00:51  阅读:165  来源: 互联网

标签:nil Goroutine sync victim New shared local pool


sync.pool

前言

  1. sync.Pool的核心作用 - 读源码,缓存稍后会频繁使用的对象+减轻GC压力
  2. sync.Pool的Put与Get - Put的顺序为local private-> local shared,Get的顺序为 local private -> local shared -> remote shared ->victim -> New
  3. 思考sync.Pool应用的核心场景 - 高频使用且生命周期短的对象,且初始化始终一致,如fmt
  4. 探索Go1.13引入victim的作用 - 了解victim cache的机制

使用

package main

import (
	"fmt"
	"reflect"
	"sync"
)

func syncPool() {
	var sp = sync.Pool{
		// Tip: 声明对象池的New函数,这里以一个简单的int为例
		New: func() interface{} {
			return 100
		},
	}
	// Tip: 从对象池里获取一个对象
	data := sp.Get().(int)
	fmt.Println(data)
	// Tip: 往对象池里放回一个对象
	sp.Put(data)

	fmt.Println(reflect.ValueOf(sp))
}

源码

Get

//Get local private -> local shared -> remote shared ->victim -> New
func (p *Pool) Get() interface{} {
	x := l.private//先从local private拿
	l.private = nil
	if x == nil {
		x, _ = l.shared.popHead()//private没有的话从local shared拿
		if x == nil {
			x = p.getSlow(pid)//local没有就从remote shared拿,再没有就从victim拿,详情看下面函数代码
		}
	}
	if x == nil && p.New != nil {
		x = p.New()//都没有只能New一个了
	}
	return x
}
func (p *Pool) getSlow(pid int) interface{} {
	// See the comment in pin regarding ordering of the loads.
	size := runtime_LoadAcquintptr(&p.localSize) // load-acquire
	locals := p.local                            // load-consume
	//这里,从其他的process shared上面取
	// Try to steal one element from other procs.
	for i := 0; i < int(size); i++ {
		l := indexLocal(locals, (pid+i+1)%int(size))
		if x, _ := l.shared.popTail(); x != nil {
			return x
		}
	}
	// Try the victim cache. We do this after attempting to steal
	// from all primary caches because we want objects in the
	// victim cache to age out if at all possible.
	size = atomic.LoadUintptr(&p.victimSize)
	if uintptr(pid) >= size {
		return nil
	}
	locals = p.victim//没有的话从victim上拿
	return nil
}

Put

// Put adds x to the pool.
//local private-> local shared
func (p *Pool) Put(x interface{}) {
	if l.private == nil {
		l.private = x
		x = nil
	}
	if x != nil {
		l.shared.pushHead(x)
	}
}

理解

sync.pool

sync.pool是一个对象池,对象池的声明,核心是一个New
从对象池获取一个对象Get,把一个对象放入对象池Put
对象池的核心作用:缓存会频繁使用的对象,减轻GC压力
local对应的就是当前goroutine运行在GMP模型中的P,process
其他几个process就是remote
长生命周期没必要使用pool,思考GC相关的问题即可
New里面是没有任何传参的,如果想要每次New出来的东西都不一样的话,是不适合用pool的

victim

victim在poolCleanup的时候被赋值,而poolCLeanup在GC的时候被调用
先把victim和victimsize情空,再把local和localsize的数据全放过来,再把local和localsize的数据放过来

func poolCleanup() {
	// This function is called with the world stopped, at the beginning of a garbage collection.
	// It must not allocate and probably should not call any runtime functions.

	// Because the world is stopped, no pool user can be in a
	// pinned section (in effect, this has all Ps pinned).

	// Drop victim caches from all pools.
	for _, p := range oldPools {
		p.victim = nil
		p.victimSize = 0
	}

	// Move primary cache to victim cache.
	for _, p := range allPools {
		p.victim = p.local
		p.victimSize = p.localSize
		p.local = nil
		p.localSize = 0
	}

	// The pools with non-empty primary caches now have non-empty
	// victim caches and no pools have primary caches.
	oldPools, allPools = allPools, nil
}

在getslow的时候,victim被使用,locals=p.victim,综合来看victim地位相当于是脏数据,使用的频率是不会太高的,
引用这个victim给sync.pool带来了怎样的提升呢?最关键的还是在减轻GC的压力,本来是直接清空local,现在只需要把先移到victim上,对victim再慢慢操作,减少GC带来的抖动这些问题

标签:nil,Goroutine,sync,victim,New,shared,local,pool
来源: https://blog.csdn.net/qq_42956653/article/details/121049858

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有