Go 語言 sync 包使用教程

Go 語言 sync 包使用教程

Go 語言的 sync 包提供了基本的同步原語,用于在并發編程中協調 goroutine 之間的操作。

1. 互斥鎖 (Mutex)

互斥鎖用于保護共享資源,確保同一時間只有一個 goroutine 可以訪問。

特點:

  • 最基本的同步原語,實現互斥訪問共享資源
  • 有兩個方法:Lock()Unlock()
  • 不可重入,同一個 goroutine 重復獲取會導致死鎖
  • 沒有超時機制,鎖定后必須等待解鎖
  • 不區分讀寫操作,所有操作都是互斥的
  • 適用于共享資源競爭不激烈的場景
  • 性能高于 channel 實現的互斥機制
  • 不保證公平性,可能導致饑餓問題
import ("fmt""sync""time"
)func main() {var mutex sync.Mutexcounter := 0for i := 0; i < 1000; i++ {go func() {mutex.Lock()defer mutex.Unlock()counter++}()}time.Sleep(time.Second)fmt.Println("計數器:", counter)
}

2. 讀寫鎖 (RWMutex)

當多個 goroutine 需要讀取而很少寫入時,讀寫鎖比互斥鎖更高效。

特點:

  • 針對讀多寫少場景優化的鎖
  • 提供四個方法:RLock()RUnlock()Lock()Unlock()
  • 允許多個讀操作并發進行,但寫操作是互斥的
  • 寫鎖定時,所有讀操作都會被阻塞
  • 有讀鎖定時,寫操作會等待所有讀操作完成
  • 寫操作優先級較高,防止寫饑餓
  • 內部使用 Mutex 實現
  • 比 Mutex 有更多開銷,但在讀多寫少場景下性能更高
var rwMutex sync.RWMutex
var data map[string]string = make(map[string]string)// 讀取操作
func read(key string) string {rwMutex.RLock()defer rwMutex.RUnlock()return data[key]
}// 寫入操作
func write(key, value string) {rwMutex.Lock()defer rwMutex.Unlock()data[key] = value
}

3. 等待組 (WaitGroup)

等待組用于等待一組 goroutine 完成執行。

特點:

  • 用于協調多個 goroutine 的完成
  • 提供三個方法:Add()Done()Wait()
  • Add() 增加計數器,參數可為負數
  • Done() 等同于 Add(-1),減少計數器
  • Wait() 阻塞直到計數器歸零
  • 計數器不能變為負數,會導致 panic
  • 可以重用,計數器歸零后可以再次增加
  • 非常適合"扇出"模式(啟動多個工作 goroutine 并等待全部完成)
  • 不包含工作內容信息,僅表示完成狀態
  • 輕量級,開銷很小
func main() {var wg sync.WaitGroupfor i := 0; i < 5; i++ {wg.Add(1) // 增加計數器go func(id int) {defer wg.Done() // 完成時減少計數器fmt.Printf("工作 %d 完成\n", id)}(i)}wg.Wait() // 等待所有 goroutine 完成fmt.Println("所有工作已完成")
}

4. 一次性執行 (Once)

Once 確保一個函數只執行一次,無論有多少 goroutine 嘗試執行它。

特點:

  • 確保某個函數只執行一次
  • 只有一個方法:Do(func())
  • 即使在多個 goroutine 中調用也只執行一次
  • 常用于單例模式或一次性初始化
  • 內部使用互斥鎖和一個標志位實現
  • 非常輕量級,幾乎沒有性能開銷
  • 如果傳入的函數 panic,視為已執行
  • 不能重置,一旦執行就不能再次執行
  • 傳入不同的函數也不會再次執行
var once sync.Once
var instance *singletonfunc getInstance() *singleton {once.Do(func() {instance = &singleton{}})return instance
}

5. 條件變量 (Cond)

條件變量用于等待或宣布事件的發生。

特點:

  • 用于等待或通知事件發生
  • 需要與互斥鎖結合使用:sync.NewCond(&mutex)
  • 提供三個方法:Wait()Signal()Broadcast()
  • Wait() 自動解鎖并阻塞,被喚醒后自動重新獲取鎖
  • Signal() 喚醒一個等待的 goroutine
  • Broadcast() 喚醒所有等待的 goroutine
  • 適合生產者-消費者模式
  • 可以避免輪詢,提高性能
  • 使用相對復雜,容易出錯
  • 等待必須在獲取鎖后調用
var mutex sync.Mutex
var cond = sync.NewCond(&mutex)
var ready boolfunc main() {go producer()// 消費者mutex.Lock()for !ready {cond.Wait() // 等待條件變為真}fmt.Println("數據已準備好")mutex.Unlock()
}func producer() {time.Sleep(time.Second) // 模擬工作mutex.Lock()ready = truecond.Signal() // 通知一個等待的 goroutine// 或使用 cond.Broadcast() 通知所有等待的 goroutinemutex.Unlock()
}

6. 原子操作 (atomic)

對于簡單的計數器或標志,可以使用原子操作包而不是互斥鎖。

特點:

  • 底層的原子操作,無鎖實現
  • 適用于簡單的計數器或標志位
  • 比互斥鎖性能更高,開銷更小
  • 提供多種原子操作:AddLoadStoreSwapCompareAndSwap
  • 支持多種數據類型:int32、int64、uint32、uint64、uintptr 和指針
  • 可用于實現自己的同步原語
  • 不適合復雜的共享狀態
  • 在多 CPU 系統上可能導致緩存一致性開銷
  • Go 1.19 引入了新的原子類型
import ("fmt""sync/atomic""time"
)func main() {var counter int64 = 0for i := 0; i < 1000; i++ {go func() {atomic.AddInt64(&counter, 1)}()}time.Sleep(time.Second)fmt.Println("計數器:", atomic.LoadInt64(&counter))
}

7. Map (sync.Map)

Go 1.9 引入的線程安全的 map。

特點:

  • Go 1.9 引入的線程安全的哈希表
  • 無需額外加鎖即可安全地并發讀寫
  • 提供五個方法:StoreLoadLoadOrStoreDeleteRange
  • 內部使用分段鎖和原子操作優化性能
  • 適用于讀多寫少的場景
  • 不保證遍歷的順序
  • 不支持獲取元素數量或判斷是否為空
  • 不能像普通 map 那樣直接使用下標語法
  • 性能比加鎖的普通 map 更好,但單線程下比普通 map 慢
  • 內存開銷較大
var m sync.Mapfunc main() {// 存儲鍵值對m.Store("key1", "value1")m.Store("key2", "value2")// 獲取值value, ok := m.Load("key1")if ok {fmt.Println("找到鍵:", value)}// 如果鍵不存在則存儲m.LoadOrStore("key3", "value3")// 刪除鍵m.Delete("key2")// 遍歷所有鍵值對m.Range(func(key, value interface{}) bool {fmt.Println(key, ":", value)return true // 返回 false 停止遍歷})
}

8. Pool (sync.Pool)

對象池用于重用臨時對象,減少垃圾回收壓力。

特點:

  • 用于緩存臨時對象,減少垃圾回收壓力
  • 提供兩個方法:Get()Put()
  • 需要提供 New 函數來創建新對象
  • 對象可能在任何時候被垃圾回收,不保證存活
  • 在 GC 發生時會清空池中的所有對象
  • 不適合管理需要顯式關閉的資源(如文件句柄)
  • 適合于頻繁創建和銷毀的對象
  • 沒有大小限制,Put 總是成功的
  • 每個 P(處理器)有自己的本地池,減少競爭
  • Go 1.13 后大幅提升了性能
var bufferPool = sync.Pool{New: func() interface{} {return new(bytes.Buffer)},
}func process() {// 獲取緩沖區buffer := bufferPool.Get().(*bytes.Buffer)buffer.Reset() // 清空以便重用// 使用緩沖區buffer.WriteString("hello")// 操作完成后放回池中bufferPool.Put(buffer)
}

9. 綜合示例

下面是一個綜合示例,展示了多個同步原語的使用:

package mainimport ("fmt""sync""time"
)type SafeCounter struct {mu sync.Mutexwg sync.WaitGroupcount int
}func main() {counter := SafeCounter{}// 啟動 5 個 goroutine 增加計數器for i := 0; i < 5; i++ {counter.wg.Add(1)go func(id int) {defer counter.wg.Done()for j := 0; j < 10; j++ {counter.mu.Lock()counter.count++fmt.Printf("Goroutine %d: 計數器 = %d\n", id, counter.count)counter.mu.Unlock()// 模擬工作time.Sleep(100 * time.Millisecond)}}(i)}// 等待所有 goroutine 完成counter.wg.Wait()fmt.Println("最終計數:", counter.count)
}

最佳實踐

  1. 使用 defer 解鎖:確保即使發生錯誤也能解鎖

    mu.Lock()
    defer mu.Unlock()
    
  2. 避免鎖的嵌套:容易導致死鎖

  3. 保持臨界區簡短:鎖定時間越短越好

  4. 基準測試比較

    • 對于大多數簡單操作,atomic 比 Mutex 快
    • RWMutex 在讀操作遠多于寫操作時優于 Mutex
    • sync.Map 在高并發下比加鎖的 map 性能更好
  5. 內存對齊

    • 原子操作需要內存對齊
    • 不正確的內存對齊會嚴重影響性能
    • 特別是在 32 位系統上使用 64 位原子操作
  6. 超時控制

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()done := make(chan struct{})
    go func() {// 執行可能耗時的操作mu.Lock()// ...mu.Unlock()done <- struct{}{}
    }()select {
    case <-done:// 操作成功完成
    case <-ctx.Done():// 操作超時
    }
    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73613.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73613.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73613.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ubuntu22.04安裝搜狗輸入法保姆教程~

一、添加中文語言支持 1.首先打開設置,找到Language and Region 2.點擊Manage Installed Languages 3.點擊 Install/Remove Languages... 4.選中Chinese (simplified),點擊Apply

docker中間件部署

1.docker安裝 # 1.卸載舊版本 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine# 2.需要的安裝包 yum install -y yum-utils# 3.設置鏡像的倉庫 # 3.1.默認是國外的&#x…

python康復日記-request庫的使用,爬蟲自動化測試

一&#xff0c;request的簡單應用 #1請求地址 URLhttps://example.com/login #2參數表單 form_data {username: admin,password: secret } #3返回的響應對象response response requests.post(URL,dataform_data,timeout5 ) #4處理返回結果&#xff0c;這里直接打印返回網頁的…

強化學習和智能決策:Q-Learning和Deep Q-Learning算法

強化學習(Reinforcement Learning, RL)是機器學習的一個重要分支,它通過智能體(Agent)與環境交互來學習最優決策策略,旨在最大化智能體的長期累積獎勵。Q-Learning和Deep Q-Learning是強化學習中的兩種關鍵算法,它們在智能決策領域發揮著重要作用。 一、強化學習基礎 …

ubuntu22.04 安裝Jitsi meet 開源會議系統,代替騰訊會議

0.安裝 官方安裝教程Self-Hosting Guide - Debian/Ubuntu server | Jitsi Meet 一定要用域名訪問&#xff0c; 一定要用域名訪問&#xff0c; 一定要用域名訪問&#xff0c; 一定要用域名訪問&#xff0c; 域名一定要有ssl證書&#xff0c;域名一定要有ssl證書&#xff0c;域名…

專家管理系統(源碼+文檔+講解+演示)

引言 在知識經濟時代&#xff0c;專家管理系統成為了企業優化知識資源、提升決策效率的重要工具。本文將介紹一款創新的專家管理系統&#xff0c;該系統通過智能化工具&#xff0c;助力企業實現專家資源的高效管理和利用。 平臺概述 專家管理系統采用前后端分離的架構設計&a…

css基礎-選擇器

選擇器進階 子串選擇器 /* 匹配 href 以 "https" 開頭的鏈接 */ a[href^"https"] {color: green; }/* 匹配 href 包含 "example" 的鏈接 */ a[href*"example"] {text-decoration: underline; }/* 匹配 href 以 ".pdf" 結尾…

Spring Boot屬性設置方法及優先級完整說明+表格對比

Spring Boot屬性設置方法及優先級完整說明 官網參考&#xff1a; https://docs.spring.io/spring-boot/3.4-SNAPSHOT/reference/features/external-config.html#features.external-config.files 屬性設置方法優先級順序&#xff08;從高到低&#xff09; 命令行參數&#xf…

上門家政小程序實戰,從0到1解決方案

一、邏輯分析 上門家政小程序主要涉及用戶端和服務端兩大部分。用戶端需要實現服務瀏覽、預約下單、訂單跟蹤等功能&#xff1b;服務端則要處理訂單管理、服務人員管理、數據統計等任務。以下是詳細的功能模塊分析&#xff1a; 用戶注冊與登錄&#xff1a;用戶通過手機號或第三…

LLVM學習-DragonEgg工具

2.2.2 使用DragonEgg和LLVM工具了解編譯流程 如果希望看到前端的運行情況&#xff0c;請使用-S -fplugin-arg-dragonegg-emit-ir標志&#xff0c;該標志將產生以LLVM IR代碼表示的人工可讀文件。 一旦編譯器將程序轉換為IR則停止編譯&#xff0c;并將內存中的表示內容寫入磁盤的…

關于cmd中出現無法識別某某指令的問題

今天來解決以下這個比較常見的問題&#xff0c;安裝各種軟件都可能會發生&#xff0c;一般是安裝時沒勾選注冊環境變量&#xff0c;導致cmd無法識別該指令。例如mysql&#xff0c;git等&#xff0c;一般初學者可能不太清楚。 解決這類問題最主要的是了解環境變量的概念&#x…

ThreadLocal詳解與高頻場景實戰指南

ThreadLocal詳解與高頻場景實戰指南 1. ThreadLocal概述 ThreadLocal是Java提供的線程本地變量機制&#xff0c;用于實現線程級別的數據隔離。每個訪問該變量的線程都會獲得獨立的變量副本&#xff0c;適用于需要避免線程間共享數據的場景。 特點&#xff1a; 線程封閉性&a…

【C++初階】---類和對象(上)

1.類的定義 1.1類的定義格式 ? class為定義類的關鍵字&#xff0c;Data為類的名字&#xff0c;{}中為類的主體&#xff0c;注意類定義結束時后?分號不能省略。類體中內容稱為類的成員&#xff1a;類中的變量稱為類的屬性或成員變量;類中的函數稱為類的?法或者成員函數。 ?…

Rust安裝并配置配置vscode編譯器

一. 下載rustup-init.exe rust下載網址&#xff1a;Getting started - Rust Programming Language 根據系統&#xff0c;選擇適合的exe文件 我選擇的的是右邊64bit的 打開下載的文件 輸入1&#xff0c;回車 二. Visual C 安裝 自動下載安裝vs 等待安裝完畢 三. Rust 安裝…

openGl片段著色器的含義

片段著色器的含義及代碼中的應用說明&#xff1a; 1. 片段著色器的基本概念 片段著色器&#xff08;Fragment Shader&#xff09;是OpenGL著色器管線中的關鍵組件&#xff0c;主要用于計算屏幕空間中每個片段&#xff08;對應像素&#xff09;的最終顏色。它是圖形渲染流程的…

事務的四大特性(ACID)詳解

事務的四大特性&#xff08;ACID&#xff09;詳解 在數據庫管理系統&#xff08;如 MySQL&#xff09;中&#xff0c;事務&#xff08;Transaction&#xff09; 是指一組要么全部執行、要么全部不執行的數據庫操作&#xff0c;通常用于確保數據的完整性和一致性。事務有四大核…

ubuntu設置開機自動運行應用

系統版本&#xff1a;Ubuntu 24.04.1 LTS桌面版 按招網上的資料顯示&#xff0c;當前版本主要的實現方式有以下兩種&#xff0c; 方式1&#xff1a;通過圖形界面的【啟動應用程序】設置開機自啟動&#xff1b;方式2&#xff1a;配置為服務實現開機自啟動。 但是在我的電腦上方…

ECharts各類炫酷圖表/3D柱形圖

一、前言 最近雞米花實現了各類的炫酷的圖表&#xff0c;有3D柱形圖、雙邊柱形圖以及異形柱形圖&#xff0c;好了&#xff0c;直接上圖&#xff1a; 二、效果圖 一個個來吧&#xff0c;下面就是代碼啦&#xff0c;注意&#xff0c;一下圖表展示的寬高均為800px*300px 三、異形橫…

機器人原點丟失后找回原點的解決方案與步驟

機器人原點丟失后找回原點的解決方案與步驟 在機器人運行過程中&#xff0c;原點丟失可能導致定位錯誤、運動失控等問題&#xff0c;常見于機械臂、AGV&#xff08;自動導引車&#xff09;、3D打印機等設備。以下是針對原點丟失問題的系統性解決方案及詳細步驟&#xff0c;涵蓋…

HCIP——園區網、VLAN

園區網 園區網搭建核心思路&#xff1a;冗余&#xff08;備份&#xff09;--- 保證其健壯性 1、設備冗余 2、線路冗余 3、網關冗余 4、ups&#xff08;不間斷電源&#xff09;冗余—— 能不斷電&#xff08;物理層&#xff09; 三層交換機和路由器的選擇&#xff1a; 三層交換…