• Golang 之 sync.Pool揭秘


    sync.Pool 主要通过减少GC来提升性能,是Goroutine并发安全的

    sync.Pool使用

    初始化Pool实例,可以通过配置new方法来声明Pool元素创建的方法

    bufferpool := &sync.Pool {
        New: func() interface {} {
            println("Create new instance")
            return struct{}{}
        }
    }

    申请和释放,申请会返回Pool中已经存在的对象,如果没有就初始化一个对象,Pool中的对象何时释放外界是不清楚的

    buffer := bufferPool.Get()
    bufferPool.Put(buffer)

    当有频繁创建的对象时,而且不用关注对象的状态,就用Pool,可以避免大量实例的创建与释放

    // 用来统计实例真正创建的次数
    var numCalcsCreated int32
    // 创建实例的函数
    func createBuffer() interface{} {
        // 这里要注意下,非常重要的一点。这里必须使用原子加,不然有并发问题
        atomic.AddInt32(&numCalcsCreated, 1)
        buffer := make([]byte, 1024)
        return &buffer
    }
    func main() {
        // 创建实例
        bufferPool := &sync.Pool{
            New: createBuffer,
        }
        numWorkers := 1024 * 1024
        var wg sync.WaitGroup
        wg.Add(numWorkers)
        for i := 0; i < numWorkers; i++ {
            go func() {
                defer wg.Done()
                // 申请一个 buffer 实例
                buffer := bufferPool.Get()
                _ = buffer.(*[]byte)
                // 释放一个 buffer 实例
                defer bufferPool.Put(buffer)
            }()
        }
        wg.Wait()
        fmt.Printf("%d buffer objects were created", numCalcsCreated)
    }
    输出
    8 buffer objects were created
    or 不固定
    7 buffer objects were created

    将bufferPool.Get变为buffer := createBuffer(),将输出1048576 buffer objects were created在程序运行时,进程消耗内存非常大,同时在内部runtime GC回收时会很影响性能

    sync.Pool线程安全吗?

    sync.Pool本身是线程安全的,get和put方法都是线程安全的,但是new函数不是线程安全的,上例中用原子加就是这个原因,不用原子加,可能最后的数字会比实际的小一点,但是在当下多核机器下几乎看不到区别

    为什么 sync.Pool 不适合用于像 socket 长连接或数据库连接池?

    Pool 池里的元素随时可能释放掉,释放策略完全由 runtime 内部管理;

    Get 获取到的元素对象可能是刚创建的,也可能是之前创建好 cache 住的。使用者无法区分;

    Pool 池里面的元素个数你无法知道;

    总结:

    sync.Pool 本质用途是增加临时对象的重用率,减少 GC 负担,

    不能对 Pool.Get 出来的对象做预判,有可能是新的(新分配的),有可能是旧的(之前人用过,然后 Put 进去的),也无法去判读啊Pool中元素个数

    sync.Pool 本身的 Get, Put 调用是并发安全的,sync.New 指向的初始化函数会并发调用,

    当用完一个从 Pool 取出的实例时候,一定要记得调用 Put,否则 Pool 无法复用这个实例,通常这个用 defer 完成,

  • 相关阅读:
    LeetCode91 Decode Ways
    LeetCode93 Restore IP Addresses
    LeetCode92 Reverse Linked List II
    LeetCode90 Subsets II
    LeetCode89 Gray Code
    最长公共子序列及其引申问题
    constexpr:编译期与运行期之间的神秘关键字
    I/O模型: 阻塞、非阻塞、I/O复用、同步、异步
    LeetCode86 Partition List
    maven 安装 过程
  • 原文地址:https://www.cnblogs.com/peterleee/p/13405915.html
Copyright © 2020-2023  润新知