sync.Pool 的分享,关于适用场景:
当多个 goroutine 都需要创建同⼀个对象的时候,如果 goroutine 数过多,导致对象的创建数⽬剧增,进⽽导致 GC 压⼒增大。形成 “并发⼤-占⽤内存⼤-GC 缓慢-处理并发能⼒降低-并发更⼤”这样的恶性循环。 在这个时候,需要有⼀个对象池,每个 goroutine 不再⾃⼰单独创建对象,⽽是从对象池中获取出⼀个对象(如果池中已经有的话)。
因此关键思想就是对象的复用,避免重复创建、销毁,下面我们来看看如何使用。
所以,sync.pool 的作用一句话描述就是,复用临时对象,以避免频繁的内存分配和回收,从而减少 GC 压力。
sync.Pool 是协程安全的,这对于使用者来说是极其方便的。使用前,设置好对象的 New 函数,用于在 Pool 里没有缓存的对象时,创建一个。之后,在程序的任何地方、任何时候仅通过 Get()、Put() 方法就可以取、还对象了。
pool就是对象缓存池,用来减少堆上内存的反复申请和释放的。因为 golang 的内存是用户触发申请,runtime 负责回收。如果用户申请内存过于频繁,会导致runtime 的回收压力陡增,从而影响整体性能。
有了pool 之后就不一样了,对象申请先看池子里有没有现成的,有就直接返回。释放的时候内存也不是直接归还,而是放进池子而已。适时释放。这样就能极大的减少申请内存的频率。从而减少gc压力。
sync.Cond 使用
Golang 的 sync 包中的 Cond 实现了一种条件变量,可以使用在多个Reader等待共享资源 ready 的场景(如果只有一读一写,一个锁或者channel就搞定了)。
Cond的汇合点:多个goroutines等待、1个goroutine通知事件发生。
比较适合任务调用场景,一个 Master goroutine 通知事件发生,多个 Worker goroutine 在资源没准备好的时候就挂起,等待通知。
// 创建Cond
cond := sync.NewCond(new(sync.Mutex))
// 挂起goroutine
cond.L.Lock()
cond.Wait()
// 唤醒一个
cond.Signal()
// 唤醒所有
cond.Broadcast()
基本使用大概是需要等待的时候通过 Wait() 将 Goroutine 挂起,资源准备好的时候再通过 Signal() 或者 Broadcast() 将挂起中的 Goroutine 唤醒。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var (
locker sync.Mutex
cond = sync.NewCond(&locker)
wg sync.WaitGroup
)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(number int) {
// wait()方法内部是先释放锁 然后在加锁 所以这里需要先 Lock()
cond.L.Lock()
defer cond.L.Unlock()
cond.Wait() // 等待通知,阻塞当前 goroutine
fmt.Printf("g %v ok~ \n", number)
wg.Done()
}(i)
}
for i := 0; i < 5; i++ {
// 每过 100毫秒 唤醒一个 goroutine
cond.Signal()
time.Sleep(time.Millisecond * 100)
}
time.Sleep(time.Millisecond * 100)
// 剩下5个 goroutine 一起唤醒
cond.Broadcast()
fmt.Println("Broadcast...")
wg.Wait()
}
func add(a, b int) int {
return a + b
}
func Fib(n int) int {
if n < 2 {
return n
}
return Fib(n-1) + Fib(n-2)
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。