天天看点

16、Go语言同步4-sync包的其他API

16、Go语言同步4-sync包的其他API

16、Go语言同步4-sync包的其他API

  • ​​1、Once 只执行一次​​
  • ​​2、对象池 sync.Pool​​

1、Once 只执行一次

sync包除了提供了互斥锁、读写锁、条件变量外,还提供了一个结构体:​

​sync.Once​

​,负责只执行一次,也即全局唯一操作。

使用方式如下:

var once sync.Once
once.Do(func(){})           // Do方法的有效调用次数永远是1      

​sync.Once​

​的典型应用场景是只执行一次的任务,如果这样的任务不适合在init函数中执行,该结构体类就会派上用场。

sync.Once内部使用了“卫述语句、双重检查锁定、共享标记的原子操作”来实现​

​Once​

​功能。

once示例:

package main

import (
    "fmt"
    "sync"
)

type Person struct {
    Name string
    Age int
}

func (p *Person)Grown() {
    p.Age += 1
}

func main() {

    var once sync.Once
    var wg sync.WaitGroup

    p := &Person{
        "比尔",
        0,
    }

    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            once.Do(func(){
                p.Grown()
            })
            wg.Done()
        }()
    }

    wg.Wait()

    fmt.Println("年龄是:", p.Age)            // 只会输出 1

}      

2、对象池 sync.Pool

​sync.Pool​

​类型可以视为临时值的容器,该容器具备自动伸缩、高效特性,同时也是并发安全的,其方法有:

  • Get:从池中取出一个interface{}类型的值
  • Put:把一个interface{}类型的值存于池中

注意:

  • 如果池子从未Put过,其New字段也没有被赋值一个非nil值,那么Get方法返回结果一定是nil。
  • Get获取的值不一定存在于池中,如果Get到的值存在于池中,则该值Get后会被删除

对象池原理:

  • 对象池可以把内部的值产生的压力分摊,即专门为每个操作它的协程关联的P建立本地池。Get方法被调用时,先从本地P对象的本地私有池和本地共享池获取值,如果获取失败,则从其他P的本地私有池偷取一个值返回,如果依然没有获取到,会依赖于当前对象池的值生成函数。注意:生产函数产生的值不会被放到对象池中,只是起到返回值作用
  • 对象池的Put方法会把参数值存放到本地P的本地池中,每个P的本地共享池中的值,都是共享的,即随时可能会被偷走
  • 对象池对垃圾回收友好,执行垃圾回收时,会将对象池中的对象值全部溢出

应用场景:存储被分配了但是未被使用,未来可能会被使用的值,以减少GC的压力。

案例:由于fmt包总是使用一些​​

​[]byte​

​​对象,golang为期建立了一个临时对象池,存放这些对象,需要的时候,从pool中取,拿不到则分配一分,这样就能避免一直生成​

​[]byte​

​,垃圾回收的效率也高了很多。

// 一个[]byte的对象池,每个对象为一个[]byte
var bytePool = sync.Pool{
  New: func() interface{} {
    b := make([]byte, 1024)
    return &b
  },
}

func main() {
  a := time.Now().Unix()
  // 不使用对象池
  for i := 0; i < 1000000000; i++{
    obj := make([]byte,1024)
    _ = obj
  }
  b := time.Now().Unix()
  // 使用对象池
  for i := 0; i < 1000000000; i++{
    obj := bytePool.Get().(*[]byte)
    _ = obj
    bytePool.Put(obj)
  }
  c := time.Now().Unix()
  fmt.Println("without pool ", b - a, "s")
  fmt.Println("with    pool ", c - b, "s")
}

// without pool  34 s
// with    pool  24 s