Golang中除了加Mutex锁以外还有哪些方式安全读写共享变量?

在 Go 语言中,除了使用 sync.Mutex 锁来保护共享变量外,还有以下几种方式可以实现安全读写共享变量:


1. 使用 sync.RWMutex(读写锁)

  • 原理
    sync.RWMutex 是一种读写锁,允许多个读操作同时进行(读锁),但写操作是互斥的(写锁)。
    • 读多写少场景:适合读操作远多于写操作的场景,能显著提高并发性能。
  • 特点
    • 读锁(RLock/RUnlock)允许多个协程同时读取共享变量。
    • 写锁(Lock/Unlock)确保同一时间只有一个协程可以写入共享变量。
  • 示例代码
    var (
        rwMutex  sync.RWMutex
        sharedVar int
    )
    
    func read() {
        rwMutex.RLock()
        defer rwMutex.RUnlock()
        fmt.Println("Read:", sharedVar)
    }
    
    func write(value int) {
        rwMutex.Lock()
        defer rwMutex.Unlock()
        sharedVar = value
        fmt.Println("Write:", sharedVar)
    }
    

2. 使用 sync/atomic(原子操作)

  • 原理
    sync/atomic 包提供了对基本类型(如 int32int64uint32 等)的原子操作,确保操作不可分割。
  • 特点
    • 高效:比锁更轻量,适合简单的计数器或标志位。
    • 局限性:仅适用于基本数据类型,无法处理复杂结构。
  • 示例代码
    var counter int64
    
    func increment(wg *sync.WaitGroup) {
        defer wg.Done()
        atomic.AddInt64(&counter, 1) // 原子加法
    }
    
    func main() {
        var wg sync.WaitGroup
        for i := 0; i < 1000; i++ {
            wg.Add(1)
            go increment(&wg)
        }
        wg.Wait()
        fmt.Println("Final Counter:", counter)
    }
    

3. 使用 Channel(通道)

  • 原理
    通过 Channel 在协程之间传递数据,将共享变量的读写操作限制在同一个协程中,避免直接共享变量。
  • 特点
    • 通信代替共享:通过 Channel 传递数据,而不是直接共享变量。
    • 线程安全Channel 的操作是线程安全的。
    • 阻塞机制:无缓冲 Channel 的发送和接收是同步的。
  • 示例代码
    func main() {
        ch := make(chan int)
        go func() {
            for i := 0; i < 5; i++ {
                ch <- i // 发送数据到 Channel
                fmt.Println("Sent:", i)
            }
            close(ch)
        }()
    
        for value := range ch {
            fmt.Println("Received:", value)
        }
    }
    

4. 使用 sync.Map(并发安全的 Map)

  • 原理
    sync.Map 是 Go 1.9 引入的并发安全 Map,适用于需要频繁读写的键值对场景。
  • 特点
    • 线程安全:支持并发读写操作。
    • 性能优化:内部使用分段锁(Sharding)减少锁竞争。
  • 示例代码
    var m sync.Map
    
    func main() {
        // 存储键值对
        m.Store("key1", "value1")
        // 读取值
        if value, ok := m.Load("key1"); ok {
            fmt.Println("Loaded:", value)
        }
        // 删除键值对
        m.Delete("key1")
    }
    

5. 使用 sync.Cond(条件变量)

  • 原理
    sync.Cond 配合 sync.Mutex 使用,用于实现更复杂的同步逻辑(如生产者-消费者模型)。
  • 特点
    • 条件等待:协程可以在条件未满足时等待,直到被通知。
    • 适用场景:需要根据特定条件控制协程执行顺序的场景。
  • 示例代码
    var (
        cond   *sync.Cond
        shared int
    )
    
    func main() {
        cond = sync.NewCond(&sync.Mutex{})
        go func() {
            cond.L.Lock()
            fmt.Println("Goroutine 1 waiting...")
            cond.Wait()
            fmt.Println("Goroutine 1 proceeding")
            cond.L.Unlock()
        }()
    
        cond.L.Lock()
        fmt.Println("Signaling all goroutines")
        cond.Broadcast() // 广播通知所有等待的协程
        cond.L.Unlock()
    }
    

总结

方法适用场景特点
sync.RWMutex读多写少的场景提高并发性能
sync/atomic简单数据类型的计数器或标志位高效,但仅限基本类型
Channel协程间通信和数据传递通过通信代替共享,线程安全
sync.Map并发键值对存储内置并发安全,适合频繁读写
sync.Cond复杂条件同步(如生产者-消费者)需配合 sync.Mutex 使用

根据具体需求选择合适的方法,例如:

  • 简单计数器:使用 sync/atomic
  • 键值对存储:使用 sync.Map
  • 生产者-消费者模型:优先选择 Channel
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值