Go语言中sync包的妙用:同步原语详解

2025-05发布6次浏览

Go语言中的sync包提供了丰富的同步原语,用于解决并发场景下的数据竞争问题。这些工具可以确保在多线程环境中对共享资源的正确访问,避免因竞态条件导致的程序错误。下面我们将详细解析sync包中常见的同步原语及其使用方法。

1. sync.Mutex:互斥锁

Mutex是Go语言中最基本的同步原语之一,用于保护临界区代码,防止多个goroutine同时访问共享资源。

使用示例

package main

import (
    "fmt"
    "sync"
)

func main() {
    var mu sync.Mutex
    counter := 0

    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            mu.Lock()
            counter++
            mu.Unlock()
        }()
    }

    wg.Wait()
    fmt.Println("Counter:", counter) // 输出应为1000
}

在上述代码中,mutex.Lock()mutex.Unlock()分别用于锁定和释放锁,确保每次只有一个goroutine能够修改counter变量。

2. sync.RWMutex:读写锁

当一个资源需要频繁读取而较少写入时,使用RWMutex会比Mutex更高效。RWMutex允许多个goroutine同时读取资源,但只允许一个goroutine写入。

使用示例

package main

import (
    "fmt"
    "sync"
)

func main() {
    var rwmu sync.RWMutex
    data := make(map[string]int)

    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            rwmu.RLock()
            value, exists := data["key"]
            if !exists {
                rwmu.RUnlock()
                rwmu.Lock()
                defer rwmu.Unlock()
                data["key"] = i
            } else {
                rwmu.RUnlock()
            }
        }(i)
    }

    wg.Wait()
    fmt.Println("Data:", data)
}

在此示例中,我们利用了RWMutex来实现一种懒加载模式,即只有在数据不存在时才进行写入操作。

3. sync.Once:一次性执行

Once确保某个操作仅执行一次,即使有多个goroutine同时调用。

使用示例

package main

import (
    "fmt"
    "sync"
)

var once sync.Once
var data string

func initialize() {
    data = "initialized"
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            once.Do(initialize)
            fmt.Println(data)
        }()
    }

    wg.Wait()
}

无论多少个goroutine调用once.Do()initialize函数只会被执行一次。

4. sync.WaitGroup:等待一组goroutine完成

WaitGroup用于等待一组goroutine完成工作后再继续执行后续逻辑。

使用示例

package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

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

此代码展示了如何使用WaitGroup协调主goroutine与子goroutine之间的执行顺序。

5. sync.Map:并发安全的Map

sync.Map是一个并发安全的map结构,适合在高并发环境下替代标准的Go map。

使用示例

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map

    m.Store("key1", "value1")
    m.Store("key2", "value2")

    if val, ok := m.Load("key1"); ok {
        fmt.Println("Key1 Value:", val)
    }

    m.Delete("key2")
}

sync.Map提供了StoreLoadDelete等方法来操作键值对,且保证线程安全。