zoukankan      html  css  js  c++  java
  • Golang 临时对象池 sync.Pool

    Go 1.3 的sync包中加入一个新特性:Pool。官方文档可以看这里http://golang.org/pkg/sync/#Pool

    这个类设计的目的是用来保存和复用临时对象,以减少内存分配,降低CG压力。

    type Pool
        func (p *Pool) Get() interface{} 
        func (p *Pool) Put(x interface{}) 
        New func() interface{}
    

    下面说说Pool的实现:

    1.定时清理

    文档上说,保存在Pool中的对象会在没有任何通知的情况下被自动移除掉。实际上,这个清理过程是在每次垃圾回收之前做的。垃圾回收是固定两分钟触发一次。而且每次清理会将Pool中的所有对象都清理掉!

    2.如何管理数据

    先看看两个数据结构

    type Pool struct { 
        local     unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal
        localSize uintptr        // size of the local array 
     
        // New optionally specifies a function to generate 
        // a value when Get would otherwise return nil. 
        // It may not be changed concurrently with calls to Get. 
        New func() interface{} 
    } 
     
    // Local per-P Pool appendix. 
    type poolLocal struct { 
        private interface{}   // Can be used only by the respective P. 
        shared  []interface{} // Can be used by any P. 
        Mutex                 // Protects shared. 
        pad     [128]byte     // Prevents false sharing. 
    }
    

      

    Pool是提供给外部使用的对象。其中的local成员的真实类型是一个poolLocal数组,localSize是数组长度。poolLocal是真正保存数据的地方。priveate保存了一个临时对象,shared是保存临时对象的数组。

    为什么Pool中需要这么多poolLocal对象呢?实际上,Pool是给每个线程分配了一个poolLocal对象。也就是说local数组的长度,就是工作线程的数量(size := runtime.GOMAXPROCS(0))。当多线程在并发读写的时候,通常情况下都是在自己线程的poolLocal中存取数据。当自己线程的poolLocal中没有数据时,才会尝试加锁去其他线程的poolLocal中“偷”数据。

    func (p *Pool) Get() interface{} { 
        if raceenabled { 
            if p.New != nil { 
                return p.New() 
            } 
            return nil 
        } 
        l := p.pin()  // 获取当前线程的poolLocal对象,也就是p.local[pid]。 
        x := l.private 
        l.private = nil 
        runtime_procUnpin() 
        if x != nil { 
            return x 
        } 
        l.Lock() 
        last := len(l.shared) - 1 
        if last >= 0 { 
            x = l.shared[last] 
            l.shared = l.shared[:last] 
        } 
        l.Unlock() 
        if x != nil { 
            return x 
        } 
        return p.getSlow() 
    }
    

    Pool.Get的时候,首先会在local数组中获取当前线程对应的poolLocal对象。如果private中有数据,则取出来直接返回。如果没有则先锁住shared,有数据则直接返回。

    为什么这里要锁住。答案在getSlow中。因为当shared中没有数据的时候,会尝试去其他的poolLocal的shared中偷数据。

    Go语言的goroutine虽然可以创建很多,但是真正能物理上并发运行的goroutine数量是有限的,是由runtime.GOMAXPROCS(0)设置的。所以这个Pool高效的设计的地方就在于将数据分散在了各个真正并发的线程中,每个线程优先从自己的poolLocal中获取数据,很大程度上降低了锁竞争。  

      

  • 相关阅读:
    免费下载小说
    前段博客云库网
    node发送邮件
    node 发送短信
    node生成uuid
    node 控制台颜色
    OfficeCommandbarDesigner20170202.rar
    OfficeCommandbarViewer20171005.rar
    VB.Net 正则表达式测试器
    Windows_Management_Instrumentation
  • 原文地址:https://www.cnblogs.com/DaBing0806/p/6934318.html
Copyright © 2011-2022 走看看