Go語言---sync 包
sync包提供基本的同步原語,如互斥鎖。 除了Once和WaitGroup型別之外,大多數型別都是供低階庫例程使用的。經過通道和通訊可以更好地完成更高級別的同步。
互斥鎖
互斥鎖用來保證在任一時刻,只能有一個執行緒函式訪問某物件,保證共享資料的安全性防止競態。Mutex 的初始值為解鎖狀態。Mutex 通常作為其它結構體的匿名欄位使用,使該結構體具有 Lock 和 Unlock 方法。 Mutex 可以安全的在多個例程中並行使用。
// Locker 介面包裝了基本的 Lock 和 UnLock 方法,用於加鎖和解鎖。 type Locker interface { Lock() Unlock() } type Mutex struct { // contains filtered or unexported fields //欄位沒有匯出 } // Lock 用於鎖住 m,如果 m 已經被加鎖,則 Lock 將被阻塞,直到 m 被解鎖。 func (m *Mutex) Lock() // Unlock 用於解鎖 m,如果 m 未加鎖,則該操作會引發 panic。 func (m *Mutex) Unlock()
程式示例:
package main import ( "sync" "fmt" ) // 示例:互斥鎖 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,+=操作不是原子操作 順序不定,不知道哪個協程先執行,但每次只有一個協程訪問變數 count.Num += i fmt.Print(count.Num, " ") count.Unlock() // 修改完畢,解鎖 done <- true }(i) } for i := 0; i < 10; i++ { <-done } fmt.Println(count.Num) // 45 }
讀寫互斥鎖
RWMutex 比 Mutex 多了一個“讀鎖定”和“讀解鎖”,可以讓多個例程同時讀取某物件。RWMutex 的初始值為解鎖狀態。RWMutex 通常作為其它結構體的匿名欄位使用。Mutex 可以安全的在多個例程中並行使用。
一次只有一個執行緒可以佔有寫模式的讀寫鎖, 但是可以有多個執行緒同時佔有讀模式的讀寫鎖,可以提高併發性。
(1)、當讀寫鎖是寫加鎖狀態時, 在這個鎖被解鎖之前, 所有試圖對這個鎖加鎖的執行緒都會被阻塞。
(2)、當讀寫鎖在讀加鎖狀態時, 所有試圖以讀模式對它進行加鎖的執行緒都可以得到訪問權, 但是如果執行緒希望以寫模式對此鎖進行加鎖, 它必須阻塞知道所有的執行緒釋放鎖。(3)、通常, 當讀寫鎖處於讀模式鎖住狀態時, 如果有另外執行緒試圖以寫模式加鎖, 讀寫鎖通常會阻塞隨後的讀模式鎖請求, 這樣可以避免讀模式鎖長期佔用, 而等待的寫模式鎖請求長期阻塞。
實用場景:讀寫鎖適合於對資料結構的讀次數比寫次數多得多的情況。 因為, 讀模式鎖定時可以共享, 以寫模式鎖住時意味著獨佔, 所以讀寫鎖又叫共享-獨佔鎖.// 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()
條件變數
條件等待當條件不滿足時通過 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 應該鎖定。
}
// 建立一個條件等待 初始化互斥鎖變數,返回Cond變數
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 // 鎖 1、保護條件變數 2、 保護等待佇列
cond := sync.NewCond(&mu)
// 讓例程去創造條件
go func() {
mu.Lock()
condition = true // 更改條件
cond.Signal() // 傳送通知:條件已經滿足
mu.Unlock()
}()
mu.Lock()
// 檢查條件是否滿足,避免虛假通知,同時避免 Signal 提前於 Wait 執行。
for !condition {
// 等待條件滿足的通知,如果收到虛假通知,則迴圈繼續等待。 =======這裡wait函式的實現 會有解鎖操作,等待喚醒等待佇列中的執行緒
cond.Wait() // 等待時 mu 處於解鎖狀態,喚醒時重新鎖定。 //wait函式實現 類似於 linux條件變數
}
fmt.Println("條件滿足,開始後續動作...")
mu.Unlock()
}
// 輸出結果:
// 條件滿足,開始後續動作...
單次執行
Once 的作用是多次呼叫但只執行一次,Once 只有一個方法,Once.Do(),向 Do 傳入一個函式,這個函式在第一次執行 Once.Do() 的時候會被呼叫,以後再執行 Once.Do() 將沒有任何動作,即使傳入了其它的函式,也不會被執行,如果要執行其它函式,需要重新建立一個 Once 物件。Once 可以安全的在多個例程中並行使用。
// 多次呼叫僅執行一次指定的函式 f
func (o *Once) Do(f func())
程式示例:
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
}
}
Map型別
Map型別跟Go語言中 map [interface {}] interface {}類似,但是對於多個goroutine併發使用而不需要額外的鎖定或同步是安全的。Map型別是定製的,主要是保證單獨使用時保證併發執行的型別安全性。
Map型別針對兩種常見使用情況進行了優化:(1)給定鍵的入口只寫入一次,但多次讀取,僅僅在快取中存在;
(2)在多個goroutine讀取,寫入和 覆蓋不相交鍵集的條目。
在這兩種情況下,與使用單獨互斥或RWMutex鎖配對的Go型別相比,使用Map型別可以顯著減少鎖競爭。
//Map型別
type Map struct {
// contains filtered or unexported fields
}
//刪除key的value值
func (m *Map) Delete(key interface{})
//讀取
func (m *Map) Load(key interface{}) (value interface{}, ok bool)
// LoadOrStore returns the existing value for the key if present.
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored
func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
//通過匿名函式f迴圈處理Map的鍵值對,返回false 就停止
func (m *Map) Range(f func(key, value interface{}) bool)
//寫入
func (m *Map) Store(key, value interface{})