golang sync模塊

  • 重複加鎖會引發死鎖,重複解鎖會引發panic
  • sync.Mutex作爲參數時的時候需要傳指針,不然就是拷貝,會引起加鎖失敗

條件變量

  • 條件變量並不是被用來保護臨界區和共享資源的,它是用於控制想要訪問共享資源線程的順序
    • 當共享資源的狀態發生變化時,它可以被用來通知被互斥鎖阻塞的線程
  • 條件變量是基於互斥鎖或讀寫鎖的,必須有他們的支持才能夠起作用

原子操作

  • 執行速度要比其他的同步工具快得多,通常會高出好幾個數量級
  • 但由於原子操作函數只支持非常有限的數據類型,所以在很多應用場景下,互斥鎖往往是更加適合的
    • 操作系統層面只對針對二進制位或整數的原子操作提供了支持
      • 因爲原子操作不能被中斷,所以它需要足夠簡單,並且要求快速
      • 如果原子操作遲遲不能完成,而它又不會被中斷,將會給計算機執行指令的效率帶來巨大的負面影響
  • sync/atomic包中的函數可以做的原子操作有: 加法(add)、比較並交換(compare and swap,簡稱 CAS)、加載(load)、存儲(store)和交換(swap)
    • CAS 操作,是有條件的交換操作,只有在條件滿足的情況下才會進行值的交換

WaitGroup

  • 適合實現一對多的 goroutine 協作流程
  • sync.waitGroup作爲參數時,應該傳入指針,或者 Done()方法
  • 計數器的值不能小於0, 這樣會引發一個 panic
    • 不適當地調用這類值的Done方法和Add方法都會如此
func main() {
  var wg sync.WaitGroup
  wg.Add(2)
  go f(&wg)
  go g(wg.Done)
  wg.Wait()
}

func f(wg *sync.WaitGroup) {
   defer wg.Done()
  //do something
}

func g(done func()) {
	defer done()
    //do something
}

併發安全的map

  • map多線程不安全,多個線程同時寫入map會panic

    • 並且不能被recover
  • 解決方案

    • 使用sync.RWMutex加鎖
      • 不推薦,鎖消耗較大
    • 使用線程安全的 sync.Map
      • 適合讀多寫少
      • 用空間換時間,內存佔用較大
      • 算法複雜度與map類型一樣都是O(1)
    • 使用 concurrent map
      • 適合讀寫都頻繁
      • 分段加鎖map
  • sync.Map的鍵和值的類型都是interface{}

    • 由於這些鍵值的實際類型只有在程序運行期間才能夠確定,所以無法在編譯期對它們進行檢查
    • 鍵類型不能是函數類型、字典類型和切片類型
    • 不正確的鍵值實際類型會引發 panic
      在這裏插入圖片描述
  • 實現原理

    • 本身確實也用到了鎖,但是,它會盡可能地避免使用鎖
    • sync.Map類型在內部使用了大量的原子操作來存取鍵和值,並使用了兩個原生的map作爲存儲介質
    • 這兩個原生字典一個被稱爲只讀字典,另一個被稱爲髒字典
      • 空間換時間。通過冗餘的兩個數據結構(read、dirty),實現加鎖對性能的影響
      • 使用只讀數據(read),避免讀寫衝突
      • 動態調整,miss次數多了之後,將dirty數據提升爲read
      • 延遲刪除。 刪除一個鍵值只是打標記,只有在提升dirty的時候才清理刪除的數據
      • 優先從read讀取、更新、刪除,因爲對read的讀取不需要鎖

在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章