go语言的锁汇总:互斥锁和读写锁

在 Go 语言中主要有以下几种锁:

**一、互斥锁(sync.Mutex)**

1. 用途:
   - 用于保证在同一时刻只有一个协程能够访问被保护的资源,防止出现数据竞争。这个是一个悲观锁
   - 常用于多个协程需要对共享变量进行读写操作的场景

2. 示例代码:

package main

import (
    "fmt"
    "sync"
)

var count int
var mutex sync.Mutex

func increment() {
    mutex.Lock()
    count++
    mutex.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            increment()
        }()
    }
    wg.Wait()
    fmt.Println(count)
}

在这个例子中,多个协程同时调用`increment`函数对共享变量`count`进行递增操作,通过互斥锁`sync.Mutex`保证了操作的原子性,避免了数据竞争。

**二、读写锁(sync.RWMutex)**

1. 用途:
   - 允许多个协程同时进行读操作,但在写操作时需要独占访问。
   - 适用于读多写少的场景,可以提高并发性能。

2. 示例代码:

package main

import (
    "fmt"
    "sync"
)

var data int
var rwMutex sync.RWMutex

func readData() int {
    rwMutex.RLock()
    defer rwMutex.RUnlock()
    return data
}

func writeData(newData int) {
    rwMutex.Lock()
    data = newData
    rwMutex.Unlock()
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            fmt.Println(readData())
        }()
    }
    wg.Add(1)
    go func() {
        defer wg.Done()
        writeData(42)
    }()
    wg.Wait()
}

这里多个协程可以同时调用`readData`进行读操作,而写操作`writeData`会独占访问资源。

`sync.Mutex`(互斥锁)和`sync.RWMutex`(读写锁)的主要区别如下:

**一、功能特性**

1. `sync.Mutex`:
   - 提供了基本的互斥功能,同一时刻只允许一个协程持有锁进行访问。
   - 无论是读操作还是写操作,只要一个协程获取了锁,其他协程无论是读还是写都必须等待。

2. `sync.RWMutex`:
   - 区分了读锁和写锁。
   - 允许多个协程同时持有读锁进行读操作。
   - 当有一个协程持有写锁时,其他协程无论是读还是写都必须等待。

**二、性能表现**

1. 读多写少的场景:
   - 如果一个程序中读操作远远多于写操作,使用`sync.RWMutex`性能会更好。因为多个读操作可以同时进行,不会像`sync.Mutex`那样每次读操作也需要等待锁的释放。
   - 例如,一个缓存系统,经常被多个协程读取数据,而写入数据的频率相对较低。在这种情况下,使用读写锁可以提高并发度,减少等待时间。

2. 写操作频繁的场景:
   - 如果写操作比较频繁,两种锁的性能差异可能不明显,甚至在某些情况下`sync.Mutex`可能表现更好。
   - 因为`sync.RWMutex`在获取写锁时需要等待所有的读锁释放,这可能会导致一定的延迟。

**三、代码示例**

以下是一个更直观的代码对比:

package main

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

// 使用互斥锁
func mutexExample() {
    var mutex sync.Mutex
    var value int

    // 模拟多个协程同时读和写
    for i := 0; i < 5; i++ {
        go func(id int) {
            mutex.Lock()
            fmt.Printf("协程 %d 读取值:%d\n", id, value)
            value++
            fmt.Printf("协程 %d 写入值后:%d\n", id, value)
            mutex.Unlock()
        }(i)
    }
}

// 使用读写锁
func rwMutexExample() {
    var rwMutex sync.RWMutex
    var value int

    // 模拟多个协程同时读和写
    for i := 0; i < 5; i++ {
        if i%2 == 0 {
            // 写操作
            go func(id int) {
                rwMutex.Lock()
                fmt.Printf("协程 %d 写入值前:%d\n", id, value)
                value++
                fmt.Printf("协程 %d 写入值后:%d\n", id, value)
                rwMutex.Unlock()
            }(i)
        } else {
            // 读操作
            go func(id int) {
                rwMutex.RLock()
                fmt.Printf("协程 %d 读取值:%d\n", id, value)
                rwMutex.RUnlock()
            }(i)
        }
    }
}

func main() {
    // 测试互斥锁
    fmt.Println("互斥锁示例:")
    mutexExample()
    time.Sleep(time.Second)

    // 测试读写锁
    fmt.Println("读写锁示例:")
    rwMutexExample()
    time.Sleep(time.Second)
}

在这个例子中,可以观察到两种锁在不同场景下的行为差异。读写锁允许同时进行读操作,而互斥锁在任何时候都只允许一个协程访问资源。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值