同步原語

Go 語言同步原語:Mutex、WaitGroup、Atomic。

WaitGroups

用於等待所有啟動的 Goroutines 完成。

當你不需要 goroutine 回傳資料時,使用 WaitGroups。

  • 是一種同步原語,允許多個 goroutines 互相等待
  • 像計數器一樣阻塞執行,直到內部計數器變為 0
  • package sync 提供基本同步功能
import "sync"

var wg sync.WaitGroup

wg.Add(int)

wg.Wait()

wg.Done()
如果 WaitGroup 明確傳遞給函式,應該使用指標傳遞。

Methods

  • Add:設定要等待的 goroutines 數量(增加計數器)

    • 參數中的整數作為計數器
    • 表示要等待的 goroutines 數量
  • Wait:阻塞執行直到內部計數器減為 0

  • Done:將 Add() 方法的內部計數減 1

    • 由 goroutine 呼叫以表示它已完成
Go WaitGroup

完整範例

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            fmt.Printf("Worker %d done\n", id)
        }(i)
    }

    wg.Wait()
    fmt.Println("All workers completed")
}

Mutexes

互斥鎖,用於保護共享資源,確保同一時間只有一個 goroutine 可以存取。

sync.Mutex

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.value++
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}

sync.RWMutex

讀寫鎖,允許多個讀取者同時存取,但寫入時獨佔。

方法說明
RLock()獲取讀鎖(可多個同時持有)
RUnlock()釋放讀鎖
Lock()獲取寫鎖(獨佔)
Unlock()釋放寫鎖
type SafeMap struct {
    mu   sync.RWMutex
    data map[string]int
}

func (m *SafeMap) Get(key string) (int, bool) {
    m.mu.RLock()
    defer m.mu.RUnlock()
    val, ok := m.data[key]
    return val, ok
}

func (m *SafeMap) Set(key string, value int) {
    m.mu.Lock()
    defer m.mu.Unlock()
    m.data[key] = value
}

Atomic Counters

原子計數器,用於無鎖的計數操作,比 Mutex 更輕量。

import "sync/atomic"

var counter int64

// 原子增加
atomic.AddInt64(&counter, 1)

// 原子讀取
value := atomic.LoadInt64(&counter)

// 原子儲存
atomic.StoreInt64(&counter, 100)

// Compare-And-Swap
atomic.CompareAndSwapInt64(&counter, old, new)

完整範例

func main() {
    var counter int64
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            atomic.AddInt64(&counter, 1)
        }()
    }

    wg.Wait()
    fmt.Println("Counter:", counter) // 1000
}

sync.Once

確保函式只執行一次,常用於單例模式或初始化。

var (
    instance *Database
    once     sync.Once
)

func GetInstance() *Database {
    once.Do(func() {
        instance = &Database{
            // 初始化...
        }
    })
    return instance
}

sync.Pool

物件池,用於重用臨時物件,減少記憶體分配和 GC 壓力。

var bufferPool = sync.Pool{
    New: func() interface{} {
        return new(bytes.Buffer)
    },
}

func process(data []byte) {
    buf := bufferPool.Get().(*bytes.Buffer)
    defer func() {
        buf.Reset()
        bufferPool.Put(buf)
    }()

    buf.Write(data)
    // 處理 buffer...
}

sync.Map

並發安全的 map,適用於讀多寫少的場景。

var m sync.Map

// 儲存
m.Store("key", "value")

// 讀取
value, ok := m.Load("key")

// 讀取或儲存(若不存在則儲存)
actual, loaded := m.LoadOrStore("key", "default")

// 刪除
m.Delete("key")

// 遍歷
m.Range(func(key, value interface{}) bool {
    fmt.Printf("%v: %v\n", key, value)
    return true // 回傳 false 停止遍歷
})
對於大多數情況,使用 map + sync.RWMutex 效能更好。sync.Map 適合 key 集合穩定且讀取頻繁的場景。

Worker Pools

工作池模式,用於管理並發任務,控制並發數量。

func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, job)
        results <- job * 2
    }
}

func main() {
    const numJobs = 10
    const numWorkers = 3

    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    // 啟動 workers
    for w := 1; w <= numWorkers; w++ {
        go worker(w, jobs, results)
    }

    // 發送任務
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    // 收集結果
    for r := 1; r <= numJobs; r++ {
        <-results
    }
}

相關主題