------------------------------------------------------------ 臨時對象池 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() } // 輸出結果: // 條件滿足,開始后續動作... ------------------------------------------------------------