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中获取数据,很大程度上降低了锁竞争。  

      

  • 相关阅读:
    A Simple Problem with Integers poj 3468 多树状数组解决区间修改问题。
    Fliptile 开关问题 poj 3279
    Face The Right Way 一道不错的尺取法和标记法题目。 poj 3276
    Aggressive cows 二分不仅仅是查找
    Cable master(二分题 注意精度)
    B. Pasha and String
    Intervals poj 1201 差分约束系统
    UITextField的快速基本使用代码块
    将UIImage转换成圆形图片image
    color转成image对象
  • 原文地址:https://www.cnblogs.com/DaBing0806/p/6934318.html
Copyright © 2011-2022 走看看