golang同步锁

  • 时间:2025-11-19 19:38 作者: 来源: 阅读:0
  • 扫一扫,手机访问
摘要:在 Golang中,同步锁主要用于多个 goroutine 并发访问共享资源时,防止数据竞争,保证线程安全。Go 提供了 sync 包来实现各种同步机制,其中最常用的是 互斥锁(Mutex) 和 读写锁(RWMutex)。 1. 互斥锁(sync.Mutex)sync.Mutex 是最基本的同步锁,用于确保同一时间只有一个 goroutine 能访问临界区(共享资源)。package main

在 Golang中,同步锁主要用于多个 goroutine 并发访问共享资源时,防止数据竞争,保证线程安全。Go 提供了 sync 包来实现各种同步机制,其中最常用的是 互斥锁(Mutex) 和 读写锁(RWMutex)。

1. 互斥锁(sync.Mutex)

sync.Mutex 是最基本的同步锁,用于确保同一时间只有一个 goroutine 能访问临界区(共享资源)。

package main

import (
    "fmt"
    "sync"
)

var (
    counter = 0
    mutex   sync.Mutex
    wg      sync.WaitGroup
)

func main() {
    for i := 0; i < 1000; i++ {
       wg.Add(1)
       go func() {
          defer wg.Done()
          // 加锁
          mutex.Lock()
          counter++
          // 解锁
          mutex.Unlock()
       }()
    }

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

特点:

  • Lock():获取锁,如果已被其他 goroutine 持有,则阻塞等待。
  • Unlock():释放锁,必须由加锁的 goroutine 调用,否则 panic。
  • 不可重入:同一个 goroutine 不能多次调用 Lock()(会导致死锁)。

2. 读写锁(sync.RWMutex)

当共享资源是“读多写少”的场景时,使用 RWMutex 可以提高并发性能。多个 goroutine 可以同时读(RLock)写操作独占(Lock),期间不允许读或写

package main

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

var (
    data  = make(map[string]int)
    rwMux sync.RWMutex
    wg    sync.WaitGroup
)

func reader(id int) {
    defer wg.Done()
    rwMux.RLock()        // 获取读锁
    value := data["key"] // 读取共享数据
    rwMux.RUnlock()      // 释放读锁
    fmt.Printf("Reader %d read value: %d
", id, value)
}

func writer() {
    defer wg.Done()
    rwMux.Lock() // 获取写锁(独占)
    data["key"]++
    time.Sleep(10 * time.Millisecond) // 模拟写操作耗时
    rwMux.Unlock()                    // 释放写锁
}

func main() {
    data["key"] = 0

    // 启动 5 个写操作
    for i := 0; i < 5; i++ {
       wg.Add(1)
       go writer()
    }

    // 启动 10 个读操作
    for i := 0; i < 10; i++ {
       wg.Add(1)
       go reader(i)
    }

    wg.Wait()
    fmt.Println("Final data:", data["key"])
}

⚠️ 注意事项

  1. 避免死锁
  • 不要重复加锁(Mutex 不可重入)
  • 避免锁顺序不当导致循环等待
// ❌ 错误:可能死锁
mutex.Lock()
mutex.Lock() // 死锁!

2. 使用 defer 确保解锁

mutex.Lock()
defer mutex.Unlock() // 即使 panic 也能释放锁
// 操作共享资源

3. 锁的粒度要小

  • 锁住的代码越少越好,避免影响性能
gomutex.Lock()
sharedData++
mutex.Unlock()
// ❌ 不要把无关操作也包在锁里
// mutex.Lock()
// sharedData++
// time.Sleep(1) // 这样会降低并发性能
// mutex.Unlock()

4. 不要复制包含锁的结构体

type Counter struct {
mu sync.Mutex
value int
}
c1 := Counter{}
c2 := c1 // ❌ 复制了 Mutex,可能导致未定义行为c2.mu.Lock()

最佳实践

  • 优先使用 defer mutex.Unlock()
  • 尽量缩小锁的范围
  • 读多写少用 RWMutex
  • 避免复制包含锁的结构体
  • 思考使用 channel 替代锁

(Go 的哲学:“不要通过共享内存来通信,而应通过通信来共享内存”)

  • 全部评论(0)
手机二维码手机访问领取大礼包
返回顶部