1. 程式人生 > 其它 >Go標準庫:29---sync包(同步基元)

Go標準庫:29---sync包(同步基元)

技術標籤:Go(標準庫)sync包(同步基元)

一、包概述

import "sync"
  • sync包提供了基本的同步基元,如互斥鎖。除了Once和WaitGroup型別,大部分都是適用於低水平程式執行緒,高水平的同步使用channel通訊更好一些
  • 本包的型別的值不應被拷貝

二、包總覽

type Locker
type Once
func (o *Once) Do(f func())
type Mutex
func (m *Mutex) Lock()
func (m *Mutex) Unlock()
type RWMutex
func (rw *RWMutex) Lock()
func (rw *RWMutex) Unlock()
func (rw *RWMutex) RLock()
func (rw *RWMutex) RUnlock()
func (rw *RWMutex) RLocker() Locker
type Cond
func NewCond(l Locker) *Cond
func (c *Cond) Broadcast()
func (c *Cond) Signal()
func (c *Cond) Wait()
type WaitGroup
func (wg *WaitGroup) Add(delta int)
func (wg *WaitGroup) Done()
func (wg *WaitGroup) Wait()
type Pool
func (p *Pool) Get() interface{}
func (p *Pool) Put(x interface{})

三、Locker介面

type Locker interface {
    Lock()
    Unlock()
}
  • Locker介面代表一個可以加鎖和解鎖的物件

四、只執行一次操作(type Once)

type Once struct {
    // 包含隱藏或非匯出欄位
}
var once sync.Once
onceBody := func() {
    fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
    go func() {
        once.Do(onceBody)
        done <- true
    }()
}
for i := 0; i < 10; i++ {
    <-done
}

func (o *Once) Do()

func (o *Once) Do(f func())
  • Do方法當且僅當第一次被呼叫時才執行函式f。換句話說,給定變數:
var once Once
  • 如果once.Do(f)被多次呼叫,只有第一次呼叫會執行f,即使f每次呼叫Do 提供的f值不同。需要給每個要執行僅一次的函式都建立一個Once型別的例項
  • Do用於必須剛好執行一次的初始化。因為f是沒有引數的,因此可能需要使用閉包來提供給Do方法呼叫:
config.once.Do(func() { config.init(filename) })
  • 因為只有f返回後Do方法才會返回,f若引起了Do的呼叫,會導致死鎖

五、互斥鎖(typeMutex

type Mutex struct {
    // 包含隱藏或非匯出欄位
}

相關方法

  • Lock():Lock方法鎖住m,如果m已經加鎖,則阻塞直到m解鎖
func (m *Mutex) Lock()
  • Unlock():Unlock方法解鎖m,如果m未加鎖會導致執行時錯誤。鎖和執行緒無關,可以由不同的執行緒加鎖和解鎖
func (m *Mutex) Unlock()

六、讀寫鎖(type RWMutex)

type RWMutex struct {
    // 包含隱藏或非匯出欄位
}
  • RWMutex是讀寫互斥鎖。該鎖可以被同時多個讀取者持有或唯一個寫入者持有。RWMutex可以建立為其他結構體的欄位;零值為解鎖狀態。RWMutex型別的鎖也和執行緒無關,可以由不同的執行緒加讀取鎖/寫入和解讀取鎖/寫入鎖
  • 關於RWMutex的介紹還可以參閱:https://dongshao.blog.csdn.net/article/details/110008656

相關方法

  • Lock():Lock方法將rw鎖定為寫入狀態,禁止其他執行緒讀取或者寫入
func (rw *RWMutex) Lock()
  • Unlock():Unlock方法解除rw的寫入鎖狀態,如果m未加寫入鎖會導致執行時錯誤
func (rw *RWMutex) Unlock()
  • RLock():RLock方法將rw鎖定為讀取狀態,禁止其他執行緒寫入,但不禁止讀取。
func (rw *RWMutex) RLock()
  • RUnlock():Runlock方法解除rw的讀取鎖狀態,如果m未加讀取鎖會導致執行時錯誤
func (rw *RWMutex) RUnlock()
  • RLocker():Rlocker方法返回一個互斥鎖,通過呼叫rw.Rlock和rw.Runlock實現了Locker介面
func (rw *RWMutex) RLocker() Locker

七、條件變數(type Cond)

type Cond struct {
    // 在觀測或更改條件時L會凍結
    L Locker
    // 包含隱藏或非匯出欄位
}
  • Cond實現了一個條件變數,一個執行緒集合地,供執行緒等待或者宣佈某事件的發生
  • 每個Cond例項都有一個相關的鎖(一般是*Mutex或*RWMutex型別的值),它必須在改變條件時或者呼叫Wait方法時保持鎖定。Cond可以建立為其他結構體的欄位,Cond在開始使用後不能被拷貝

相關函式與方法

  • NewCond():使用鎖l建立一個*Cond
func NewCond(l Locker) *Cond
  • Broadcast():Broadcast喚醒所有等待c的執行緒。呼叫者在呼叫本方法時,建議(但並非必須)保持c.L的鎖定
func (c *Cond) Broadcast()
  • Signal():Signal喚醒等待c的一個執行緒(如果存在)。呼叫者在呼叫本方法時,建議(但並非必須)保持c.L的鎖定
func (c *Cond) Signal()
  • Wait():Wait自行解鎖c.L並阻塞當前執行緒,在之後執行緒恢復執行時,Wait方法會在返回前鎖定c.L。和其他系統不同,Wait除非被Broadcast或者Signal喚醒,不會主動返回
func (c *Cond) Wait()
  • 因為執行緒中Wait方法是第一個恢復執行的,而此時c.L未加鎖。呼叫者不應假設Wait恢復時條件已滿足,相反,呼叫者應在迴圈中等待:
c.L.Lock()
for !condition() {
    c.Wait()
}
... make use of condition ...
c.L.Unlock()

八、type WaitGroup

type WaitGroup struct {
    // 包含隱藏或非匯出欄位
}
  • WaitGroup用於等待一組執行緒的結束。父執行緒呼叫Add方法來設定應等待的執行緒的數量。每個被等待的執行緒在結束時應呼叫Done方法。同時,主執行緒裡可以呼叫Wait方法阻塞至所有執行緒結束
  • 關於WaitGroup的介紹還可以參閱:https://dongshao.blog.csdn.net/article/details/110329728
  • 例如:
var wg sync.WaitGroup
var urls = []string{
    "http://www.golang.org/",
    "http://www.google.com/",
    "http://www.somestupidname.com/",
}
for _, url := range urls {
    // Increment the WaitGroup counter.
    wg.Add(1)
    // Launch a goroutine to fetch the URL.
    go func(url string) {
        // Decrement the counter when the goroutine completes.
        defer wg.Done()
        // Fetch the URL.
        http.Get(url)
    }(url)
}
// Wait for all HTTP fetches to complete.
wg.Wait()

相關方法

  • Add():Add方法向內部計數加上delta,delta可以是負數;如果內部計數器變為0,Wait方法阻塞等待的所有執行緒都會釋放,如果計數器小於0,方法panic。注意Add加上正數的呼叫應在Wait之前,否則Wait可能只會等待很少的執行緒。一般來說本方法應在建立新的執行緒或者其他應等待的事件之前呼叫
func (wg *WaitGroup) Add(delta int)
  • Done()​​​​​​​:Done方法減少WaitGroup計數器的值,應線上程的最後執行
func (wg *WaitGroup) Done()
  • Wait():Wait方法阻塞直到WaitGroup計數器減為0
func (wg *WaitGroup) Wait()

九、type Pool

type Pool struct {
    // 可選引數New指定一個函式在Get方法可能返回nil時來生成一個值
    // 該引數不能在呼叫Get方法時被修改
    New func() interface{}
    // 包含隱藏或非匯出欄位
}
  • Pool是一個可以分別存取的臨時物件的集合。
  • Pool中儲存的任何item都可能隨時不做通告的釋放掉。如果Pool持有該物件的唯一引用,這個item就可能被回收。
  • Pool可以安全的被多個執行緒同時使用。
  • Pool的目的是快取申請但未使用的item用於之後的重用,以減輕GC的壓力。也就是說,讓建立高效而執行緒安全的空閒列表更容易。但Pool並不適用於所有空閒列表。
  • Pool的合理用法是用於管理一組靜靜的被多個獨立併發執行緒共享並可能重用的臨時item。Pool提供了讓多個執行緒分攤記憶體申請消耗的方法。
  • Pool的一個好例子在fmt包裡。該Pool維護一個動態大小的臨時輸出快取倉庫。該倉庫會在過載(許多執行緒活躍的列印時)增大,在沉寂時縮小。
  • 另一方面,管理著短壽命物件的空閒列表不適合使用Pool,因為這種情況下記憶體申請消耗不能很好的分配。這時應該由這些物件自己實現空閒列表。

相關方法

  • Get():
    • Get方法從池中選擇任意一個item,刪除其在池中的引用計數,並提供給呼叫者。Get方法也可能選擇無視記憶體池,將其當作空的。呼叫者不應認為Get的返回這和傳遞給Put的值之間有任何關係。
    • 假使Get方法沒有取得item:如p.New非nil,Get返回呼叫p.New的結果;否則返回nil
func (p *Pool) Get() interface{}
  • Put():Put方法將x放入池中
func (p *Pool) Put(x interface{})