天天看點

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