Golang學習 - sync 包

------------------------------------------------------------

臨時對象池

  Pool 用於存儲臨時對象,它將使用完畢的對象存入對象池中,在須要的時候取出來重複使用,目的是爲了不重複建立相同的對象形成 GC 負擔太重。其中存放的臨時對象隨時可能被 GC 回收掉(若是該對象再也不被其它變量引用)。

  從 Pool 中取出對象時,若是 Pool 中沒有對象,將返回 nil,可是若是給 Pool.New 字段指定了一個函數的話,Pool 將使用該函數建立一個新對象返回。

  Pool 能夠安全的在多個例程中並行使用,但 Pool 並不適用於全部空閒對象,Pool 應該用來管理併發的例程共享的臨時對象,而不該該管理短壽命對象中的臨時對象,由於這種狀況下內存不能很好的分配,這些短壽命對象應該本身實現空閒列表。

  Pool 在開始使用以後,不能再被複制。

------------------------------

type Pool struct {
	// 建立臨時對象的函數
	New func() interface{}
}

// 向臨時對象池中存入對象
func (p *Pool) Put(x interface{})

// 從臨時對象池中取出對象
func (p *Pool) Get() interface{}

------------------------------------------------------------

單次執行

  Once 的做用是屢次調用但只執行一次,Once 只有一個方法,Once.Do(),向 Do 傳入一個函數,這個函數在第一次執行 Once.Do() 的時候會被調用,之後再執行 Once.Do() 將沒有任何動做,即便傳入了其它的函數,也不會被執行,若是要執行其它函數,須要從新建立一個 Once 對象。

  Once 能夠安全的在多個例程中並行使用。

------------------------------

// 屢次調用僅執行一次指定的函數 f
func (o *Once) Do(f func())

------------------------------

// 示例:Once
func main() {
	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
	}
}

// 輸出結果:
// Only once

------------------------------------------------------------

互斥鎖

  互斥鎖用來保證在任一時刻,只能有一個例程訪問某對象。Mutex 的初始值爲解鎖狀態。Mutex 一般做爲其它結構體的匿名字段使用,使該結構體具備 Lock 和 Unlock 方法。

  Mutex 能夠安全的在多個例程中並行使用。

------------------------------

// Locker 接口包裝了基本的 Lock 和 UnLock 方法,用於加鎖和解鎖。
type Locker interface {
    Lock()
    Unlock()
}

// Lock 用於鎖住 m,若是 m 已經被加鎖,則 Lock 將被阻塞,直到 m 被解鎖。
func (m *Mutex) Lock()

// Unlock 用於解鎖 m,若是 m 未加鎖,則該操做會引起 panic。
func (m *Mutex) Unlock()

------------------------------

// 示例:互斥鎖
type SafeInt struct {
	sync.Mutex
	Num int
}

func main() {
	count := SafeInt{}
	done := make(chan bool)
	for i := 0; i < 10; i++ {
		go func(i int) {
			count.Lock() // 加鎖,防止其它例程修改 count
			count.Num += i
			fmt.Print(count.Num, " ")
			count.Unlock() // 修改完畢,解鎖
			done <- true
		}(i)
	}
	for i := 0; i < 10; i++ {
		<-done
	}
}

// 輸出結果(不固定):
// 2 11 14 18 23 29 36 44 45 45

------------------------------------------------------------

讀寫互斥鎖

  RWMutex 比 Mutex 多了一個「讀鎖定」和「讀解鎖」,可讓多個例程同時讀取某對象。RWMutex 的初始值爲解鎖狀態。RWMutex 一般做爲其它結構體的匿名字段使用。

  Mutex 能夠安全的在多個例程中並行使用。

------------------------------

// Lock 將 rw 設置爲寫鎖定狀態,禁止其餘例程讀取或寫入。
func (rw *RWMutex) Lock()

// Unlock 解除 rw 的寫鎖定狀態,若是 rw 未被寫鎖定,則該操做會引起 panic。
func (rw *RWMutex) Unlock()

// RLock 將 rw 設置爲讀鎖定狀態,禁止其餘例程寫入,但能夠讀取。
func (rw *RWMutex) RLock()

// Runlock 解除 rw 的讀鎖定狀態,若是 rw 未被讀鎖頂,則該操做會引起 panic。
func (rw *RWMutex) RUnlock()

// RLocker 返回一個互斥鎖,將 rw.RLock 和 rw.RUnlock 封裝成了一個 Locker 接口。
func (rw *RWMutex) RLocker() Locker

------------------------------------------------------------

組等待

  WaitGroup 用於等待一組例程的結束。主例程在建立每一個子例程的時候先調用 Add 增長等待計數,每一個子例程在結束時調用 Done 減小例程計數。以後,主例程經過 Wait 方法開始等待,直到計數器歸零才繼續執行。

------------------------------

// 計數器增長 delta,delta 能夠是負數。
func (wg *WaitGroup) Add(delta int)

// 計數器減小 1
func (wg *WaitGroup) Done()

// 等待直到計數器歸零。若是計數器小於 0,則該操做會引起 panic。
func (wg *WaitGroup) Wait()

------------------------------

// 示例:組等待
func main() {
	wg := sync.WaitGroup{}
	wg.Add(10)
	for i := 0; i < 10; i++ {
		go func(i int) {
			defer wg.Done()
			fmt.Print(i, " ")
		}(i)
	}
	wg.Wait()
}

// 輸出結果(不固定):
// 9 3 4 5 6 7 8 0 1 2

------------------------------------------------------------

條件等待

  條件等待經過 Wait 讓例程等待,經過 Signal 讓一個等待的例程繼續,經過 Broadcast 讓全部等待的例程繼續。

  在 Wait 以前應當手動爲 c.L 上鎖,Wait 結束後手動解鎖。爲避免虛假喚醒,須要將 Wait 放到一個條件判斷循環中。官方要求的寫法以下:

c.L.Lock()
for !condition() {
    c.Wait()
}
// 執行條件知足以後的動做...
c.L.Unlock()

  Cond 在開始使用以後,不能再被複制。

------------------------------

type Cond struct {
    L Locker // 在「檢查條件」或「更改條件」時 L 應該鎖定。
} 

// 建立一個條件等待
func NewCond(l Locker) *Cond

// Broadcast 喚醒全部等待的 Wait,建議在「更改條件」時鎖定 c.L,更改完畢再解鎖。
func (c *Cond) Broadcast()

// Signal 喚醒一個等待的 Wait,建議在「更改條件」時鎖定 c.L,更改完畢再解鎖。
func (c *Cond) Signal()

// Wait 會解鎖 c.L 並進入等待狀態,在被喚醒時,會從新鎖定 c.L
func (c *Cond) Wait()

------------------------------

// 示例:條件等待
func main() {
	condition := false // 條件不知足
	var mu sync.Mutex
	cond := sync.NewCond(&mu)
	// 讓例程去創造條件
	go func() {
		mu.Lock()
		condition = true // 更改條件
		cond.Signal()    // 發送通知:條件已經知足
		mu.Unlock()
	}()
	mu.Lock()
	// 檢查條件是否知足,避免虛假通知,同時避免 Signal 提早於 Wait 執行。
	for !condition {
		// 等待條件知足的通知,若是收到虛假通知,則循環繼續等待。
		cond.Wait() // 等待時 mu 處於解鎖狀態,喚醒時從新鎖定。
	}
	fmt.Println("條件知足,開始後續動做...")
	mu.Unlock()
}

// 輸出結果:
// 條件知足,開始後續動做...

------------------------------------------------------------



相關文章
相關標籤/搜索